klg2

来源:互联网 发布:oppo手机还原网络设置 编辑:程序博客网 时间:2024/06/04 18:04

FILE *fopen( const char *filename, constchar *mode );

FILE *_wfopen( const wchar_t *filename,const wchar_t *mode );

int fseek( FILE *stream, long offset, intorigin );

size_t fread( void *buffer, size_tsize, size_tcount, FILE *stream);

:文件指针指向文件/流。位置指针指向文件内部的字节位置,随着文件的读取会移动,文件指针如果不重新赋值将不会改变指向别的文件。

CBitmap::LoadBitmap

BOOL LoadBitmap( LPCTSTR lpszResourceName );

BOOL LoadBitmap( UINT nIDResource );

CDC::CreateCompatibleDC 

virtual BOOL CreateCompatibleDC(CDC* pDC );

CDC::SelectObject 

CPen* SelectObject( CPen* pPen );

CBrush* SelectObject( CBrush* pBrush );

virtual CFont* SelectObject( CFont* pFont );

CBitmap* SelectObject( CBitmap* pBitmap );

int SelectObject(CRgn* pRgn );

CDC::BitBlt

BOOL BitBlt( int x,int y, intnWidth, intnHeight, CDC* pSrcDC, int xSrc,int ySrc, DWORDdwRop );

CDocument* GetDocument( ) const;

BMP文件解析

 

一、简介

BMP(Bitmap-File)图形文件是Windows采用的图形文件格式,在Windows环境下运行的所有图象处理软件都支持BMP图像文件格式。Windows系统内部各图像绘制操作都是以BMP为基础的。Windows 3.0以前的BMP图文件格式与显示设备有关,因此把这种BMP图像文件格式称为设备相关位图DDB(device-dependent bitmap)文件格式。Windows 3.0以后的BMP图象文件与显示设备无关,因此把这种BMP图像文件格式称为设备无关位图DIB(device-independent bitmap)格式(注:Windows3.0以后,在系统中仍然存在DDB位图,像BitBlt()这种函数就是基于DDB位图的,只不过如果你想将图像以BMP格式保存到磁盘文件中时,微软极力推荐你以DIB格式保存),目的是为了让Windows能够在任何类型的显示设备上显示所存储的图象。BMP位图文件默认的文件扩展名是BMP或者bmp(有时它也会以.DIB或.RLE作扩展名)。

Bmp文件是非常常用的位图文件。针对bmp文件的处理也有一堆现成的API进行调用,然而文件内部究竟怎样,如何自己来解析这样的文件呢?在解析格式之前,将WINDOWS编程中使用的类型名解释一下。

typedef unsigned short  WORD;

typedef unsigned char   BYTE;

typedef unsigned long   DWORD;

(注:这里用到了无符号类型,在进行运算时注意数据类型的匹配与转换,否则将出现奇怪的结果。)

 

二、数据格式解析

第一块是bmp的文件头用于描述整个bmp文件的情况。结构如下:

typedef struct tagBITMAPFILEHEADER {

  WORD    bfType; //指定文件类型   

  DWORD   bfSize; //指定文件大小

  WORD    bfReserved1; //为保留字,不用考虑

  WORD    bfReserved2; //为保留字,不用考虑

  DWORD   bfOffBits; //图像开始处的字节偏移

} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

这些信息相当有用,如果你想直接来解析bmp文件。第一个bfType用于表示文件类型,如果它是bmp文件,那么它这个位置的值一定是

‘BM’ :Windows3.1x, 95, NT,

‘BA’ :OS/2Bitmap Array

‘CI’ :OS/2 ColorIcon

‘CP’ :OS/2 ColorPointer

‘IC’ :OS/2 Icon

‘PT’ :OS/2Pointer

(注:因为OS/2系统并没有被普及开,所以在编程时,你只需判断第一个标识“BM”就行。)第二个bfSize表示整个文件的字节数。第三第四个则保留,目前无意义。最后一个相当重要,表示,位图的数据信息离文件头的偏移量,以字节为单位,通常是前三个部分的长度之和。信息头的长度通常是14B(注:在Windows95、98、2000等操作系统中,位图信息头的长度并不一定是14B,因为微软已经制定出了新的BMP文件格式,其中的信息头结构变化比较大,长度加长。所以最好不要直接使用常数14B,而是应该从具体的文件中读取这个值。这样才能确保程序的兼容性。)如果信息头的长度是14B的话,这个偏移量就是54B+调色板的长度或者颜色掩码的长度。

 

第二块是位图信息头,即BITMAPINFOHEADER,用于描述整个位图文件的情况。

typedef struct tagBITMAPINFOHEADER{

  DWORD  biSize; //表示本结构的大小,40B

  LONG   biWidth; //位图的宽度

  LONG   biHeight; //位图的高度

WORD   biPlanes; //位面数永远为1参见msdn解释

  WORD   biBitCount;//位图的位数  分为1 4 8 16 24 32

  DWORD  biCompression; //本以为压缩类型,但是却另外有作用,稍候解释

  DWORD  biSizeImage; //表示位图数据区域的大小以字节为单位

  LONG   biXPelsPerMeter; //表示显示设备的水平分辨率

  LONG   biYPelsPerMeter; //表示显示设备的垂直分辨率

  DWORD  biClrUsed; //实际使用的颜色数目,通常为0,表示全部使用

  DWORD  biClrImportant; //重要的颜色数量,通常为0,表示全部重要

} BITMAPINFOHEADER, *PBITMAPINFOHEADER;

biBitCount可以取下列值:

– 双色位图(缺省情况下是黑色和白色。你可以自己定义这两种颜色)

4 - 16 色位图

8 - 256 色位图

16 - 16bit 高彩色位图,(用16位表示三种颜色,有555,565两种方式)

24 - 24bit 真彩色位图

32 - 32bit 增强型真彩色位图

biCompression可以取下列值:

0 - 不压缩 (使用BI_RGB表示)

1 - RLE 8-使用8位RLE压缩方式(用BI_RLE8表示)

2 - RLE 4-使用4位RLE压缩方式(用BI_RLE4表示)

3 - Bitfields-位域存放方式(用BI_BITFIELDS表示)

 

    第三块就是调色板或者掩码部分。如果是1、4、8则存放调色板;24与32位位图则存放RGB颜色的掩码,有3个,这些掩码用DWORD大小来存放。16位有存放调色版的也有存放掩码的。调色板的结构如下:

typedef struct tagRGBQUAD {

BYTE rgbBlue; //蓝色分量

BYTE rgbGreen; //绿色分量

BYTE rgbRed; //红色分量

BYTE rgbReserved; //保留值,通常为0

} RGBQUAD;

对于有调色板位图,调色板数量的计算公式是: (pow(2,bmpInfoH.biBitCount)) 。但是如果biClrUsed不是0,调色板数目就是biClrUsed。

typedef struct bi_bitField{

DWORD  redCode;

DWORD  greenCode

DWORD  blueCode;

} bitField;

bitField即颜色掩码其实没有什么用处。

 

    第四块就是位图的数据实体。对与有调色板的位图,表示颜色及亮度的位图数据实体存放的实际是本位置应有的颜色值在调色板数组中的位置,即索引值。位图像素区是按行存放的,每行从左到右与显示出来的图像对应;但是上下确是倒置的,即显示在上边的像素存放在后边。值得注意的一个问题是每行存放的字节数是4的整数倍。那么图像每行颜色与亮度信息存放的字节数storewidth是大于等于biWidth* biBitCount/8的最小4的整数倍。如果计算有误读入或者写入的图像将会是错位的。现在给出一个通用的公式: 4*((biWidth*biBitCoun+31)/32);

 

有了上面的知识,现在给出一c++中读入位图的函数:

为了方便给出符合c++中结构体特性的位图五种结构的定义;

typedef struct bi_bitField

{

    DWORD  redCode;

    DWORD  greenCode;

    DWORD  blueCode;

    const struct bi_bitField &operator = (const struct bi_bitField & F)

    {

        if (&F!= this)

        {

            redCode= F.redCode;

            greenCode= F.greenCode;

            blueCode= F.blueCode;

        }

        return*this;

    }

    friend std::istream &operator>>(std::istream & is,struct bi_bitField & F)

    {

        is >>F.redCode >> F.greenCode >> F.blueCode ;

        return is;

    }

 

    friend std::ostream &operator<<(std::ostream & os,const struct bi_bitField & F)

    {

        os <<F.redCode << F.greenCode << F.blueCode ;

        return os;

    }

}bitField;

 

//RGB颜色结构

typedef bitField ColorRGB;

 

//位图文件头

typedef struct tagBmpFile

{

    WORD    bfType;

    DWORD   bfSize;

    WORD    bfReserved1;

    WORD    bfReserved2;

    DWORD   bfOffBits;

    const struct tagBmpFile &operator = (const struct tagBmpFile & F)

    {

        if (&F!= this)

        {

            bfType= F.bfType;

            bfSize= F.bfSize;

            bfReserved1= F.bfReserved1;

            bfReserved2= F.bfReserved2;

            bfOffBits= F.bfOffBits;

        }

        return*this;

    }

 

    friend std::istream &operator>>(std::istream & is,struct tagBmpFile & F)

    {

        is >>F.bfType >> F.bfSize >> F.bfReserved1 >> F.bfReserved2>> F.bfOffBits;

        return is;

    }

 

    friend std::ostream &operator<<(std::ostream & os,const struct tagBmpFile & F)

    {

        os <<F.bfType << F.bfSize << F.bfReserved1 << F.bfReserved2<< F.bfOffBits;

        return os;

    }

}bmpFile;

 

//位图信息头

typedef struct tagBmpInfo

{

    DWORD   biSize;

    DWORD   biWidth;

    DWORD   biHeight;

    WORD    biPlanes;

    WORD    biBitCount;

    DWORD   biCompression;

    DWORD   biSizeImage;

    DWORD   biXPelsPerMeter;

    DWORD   biYPelsPerMeter;

    DWORD   biClrUsed;

    DWORD   biClrImportant;

    const struct tagBmpInfo &operator = (const struct tagBmpInfo & I)

    {

        if (&I!= this)

        {

            biSize= I.biSize;

            biWidth= I.biWidth;

            biHeight= I.biHeight;

            biPlanes= I.biPlanes;

            biBitCount= I.biBitCount;

            biCompression= I.biCompression;

            biSizeImage= I.biSizeImage;

            biXPelsPerMeter= I.biXPelsPerMeter;

            biYPelsPerMeter= I.biYPelsPerMeter;

            biClrUsed= I.biClrUsed;

            biClrImportant= I.biClrImportant;

        }

        return*this;

    }

 

    friend std::istream &operator>>(std::istream & is,struct tagBmpInfo & F)

    {

        is >>F.biSize

        >>   F.biWidth

        >>   F.biHeight

        >>   F.biPlanes

        >>   F.biBitCount

        >>   F.biCompression

        >>   F.biSizeImage

        >>   F.biXPelsPerMeter

        >>   F.biYPelsPerMeter

        >>   F.biClrUsed

        >>   F.biClrImportant;

        return is;

    }

 

    friend std::ostream &operator<<(std::ostream & os,const struct tagBmpInfo & F)

    {

        os  <<F.biSize

        <<   F.biWidth

        <<   F.biHeight

        <<   F.biPlanes

        <<   F.biBitCount

        <<   F.biCompression

        <<   F.biSizeImage

        <<   F.biXPelsPerMeter

        <<   F.biYPelsPerMeter

        <<   F.biClrUsed

        <<   F.biClrImportant;

        return os;

    }

}bmpInfo;

 

 

//调色板

typedef struct tagRGBQuad

{

    BYTE rgbBlue;

    BYTE rgbGreen;

    BYTE rgbRed;

    BYTE rgbReserved;

    const struct tagRGBQuad &operator = (const struct tagRGBQuad & Q)

    {

        if (&Q!= this)

        {

            rgbBlue= Q.rgbBlue;

            rgbGreen= Q.rgbGreen;

            rgbRed= Q.rgbRed;

            rgbReserved= Q.rgbReserved;

        }

        return*this;

    }

 

    friend std::istream &operator>>(std::istream & is,struct tagRGBQuad & F)

    {

        is >>F.rgbBlue

        >>F.rgbGreen

        >>F.rgbRed

        >>F.rgbReserved;

        return is;

    }

 

    friend std::ostream &operator<<(std::ostream & os,const struct tagRGBQuad & F)

    {

        os <<F.rgbBlue

        <<F.rgbGreen

        <<F.rgbRed

        <<F.rgbReserved;

        return os;

    }

} RGBQuad;

 

bool openBmp(const std::string fname)

std::string    fileName;

bmpFile        bmpFileH;

bmpInfo        bmpInfoH;

DWORD          storeWidth;

bitField       bmpBitField;

RGBQuad      * bmpRGB;

BYTE         *bmpData;

{

    FILE * inbmpF;

    WORD  colorCount;

 

    if((inbmpF=fopen(fname.c_str(),"rb"))==NULL)

        returnfalse;

    else

    {

        fileName.assign(fname);

 

        fread(&bmpFileH.bfType,2,1,inbmpF);

        fread(&bmpFileH.bfSize,4,1,inbmpF);

        fread(&bmpFileH.bfReserved1,2,1,inbmpF);

        fread(&bmpFileH.bfReserved2,2,1,inbmpF);

        fread(&bmpFileH.bfOffBits,4,1,inbmpF);

 

        //fread(&bmpFileH,14,1,inbmpF);

        fread(&bmpInfoH,40,1,inbmpF);

        if(0<bmpInfoH.biBitCount&&bmpInfoH.biBitCount<=8)

            colorCount=(WORD)(pow(2,bmpInfoH.biBitCount));

        else

            colorCount=0;

 

        storeWidth=4*((bmpInfoH.biWidth*bmpInfoH.biBitCount+31)/32);

        bmpFileH.bfSize=54+4*colorCount+storeWidth*bmpInfoH.biHeight;

 

        if ( 0 !=colorCount )

        {

            bmpRGB= new RGBQuad[colorCount];

            fread(bmpRGB,4,colorCount,inbmpF);

        }

        else

        {

            fread(&bmpBitField,12,1,inbmpF);

        }

 

        bmpInfoH.biSizeImage= storeWidth*bmpInfoH.biHeight;

        bmpData=newBYTE[bmpInfoH.biSizeImage];

 

        fseek(inbmpF,0-bmpInfoH.biSizeImage,SEEK_END);

        fread(bmpData,bmpInfoH.biSizeImage,1,inbmpF);

        fclose(inbmpF);

        returntrue;

    }

}

 

 

三、颜色及亮度信息解析

biBitCount=1

表示位图最多有两种颜色,缺省情况下是黑色和白色,你也可以自己定义这两种颜色。图像信息头装调色板中将有两个调色板项,称为索引0和索引1。图象数据阵列中的每一位表示一个象素。如果一个位是0,显示时就使用索引0的RGB值,如果位是1,则使用索引1的RGB值。

biBitCount=4

表示位图最多有2^4=16种颜色。每个象素用4位表示,并用这4位作为彩色表的索引来查找该象素的颜色。例如,如果位图中的第一个字节为0x1A,它表示两个象素,第一象素的颜色就在彩色表的第2项中,而第二个象素的颜色就在彩色表的第11项中。调色板中缺省情况下会有16个RGB项,对应于索引0到索引15。

biBitCount=8

表示位图最多有2^8种颜色。每个象素用8位表示,并用这8位作为彩色表的索引来查找该象素的颜色。例如,如果位图中的第一个字节为0x1A,这个象素的颜色就在彩色表的第26项中。此时,缺省情况下,调色板中会有256个RGB,对应于索引0到索引255。

biBitCount=16

表示位图最多有2^16种颜色。每个像素用16位(2个字节)表示。这种格式叫作高彩色,或叫增强型16位色,或64K色。它的情况比较复杂,当biCompression成员的值是BI_RGB时,它没有调色板。16位中,最低的5位表示蓝色分量,中间的5位表示绿色分量,高的5位表示红色分量,一共占用了15位,最高的一位保留,设为0。这种格式也被称作555 16位位图。

如果biCompression成员的值是BI_BITFIELDS,那么情况就复杂了,首先是原来调色板的位置被三个DWORD变量占据,称为红、绿、蓝掩码。分别用于描述红、绿、蓝分量在16位中所占的位置。在Windows 95(或98)中,系统可接受两种格式的位域:555和565,在555格式下,红、绿、蓝的掩码分别是:0x7C00、0x03E0、0x001F,而在565格式下,它们则分别为:0xF800、0x07E0、0x001F。你在读取一个像素之后,可以分别用掩码“与”上像素值,从而提取出想要的颜色分量(当然还要再经过适当的左右移操作)。在NT系统中,则没有格式限制,只不过要求掩码之间不能有重叠。(注:这种格式的图像使用起来是比较麻烦的,不过因为它的显示效果接近于真彩,而图像数据又比真彩图像小的多,所以,它更多的被用于游戏软件)。我们只需要读取其中的R或者G的掩码,来判断是那种格式。以红色掩码为例0111110000000000的时候就是555格式 1111100000000000就是565格式。

555 格式 xrrrrrgggggbbbbb

565 格式 rrrrrggggggbbbbb

解析555格式的代码:

BYTE b= bmpData[i*storeWidth+j]&0x1F;

BYTE g=((bmpData[i*storeWidth+j+1]<<6)>>3)+(buffer[i*storeWidth+j]>>5);

BYTE r=(bmpData[i*storeWidth+j +1]<<1)>>3;

有一点值得提醒的是由于有较多的位操作 ,所以在处理的时候在前一次操作的上面加上一对括号。

现在我们得到了55RGB各自的分量,但是还有一个新的问题,那就是由于两字节表示了3个颜色  555下每个颜色最多到0x1F。所以我们需要一个转换,很简单将得到的各颜色分量乘8就可以了。

 

以下是565格式时的数据分离:

BYTE b= bmpData[i*storeWidth+j]&0x1F;

BYTEg=((bmpData[i*storeWidth+j+1]<<5)>>2)+(buffer[i*storeWidth+j]>>5);

BYTE r=bmpData[i*storeWidth+j +1]>>3;

 

现在我们得到了565RGB各自的分量,但是仍然还有一个新的问题,565格式下最大的绿色分量也就0x3F。所以我们需要一个转换,很简单将得到的绿色分量乘以4,其余乘8就可以了。

biBitCount=24

表示位图最多有2^24种颜色。这种位图没有调色板。每3个字节代表一个象素,分别对应于颜色B、G、R。

biBitCount=32

表示位图最多有2^32种颜色。这种位图的结构与16位位图结构非常类似,当biCompression成员的值是BI_RGB时,它也没有调色板,32位中有24位用于存放RGB值,顺序是:最前一字节保留,红8位、绿8位、蓝8位。这种格式也被成为888 32位图。如果biCompression成员的值是BI_BITFIELDS时,原来调色板的位置将被三个DWORD变量占据,成为红、绿、蓝掩码,分别用于描述红、绿、蓝分量在32位中所占的位置。在Windows 95(or98)中,系统只接受888格式,也就是说三个掩码的值将只能是:0xFF0000、0xFF00、0xFF。而在NT系统中,你只要注意使掩码之间不产生重叠就行。

 

在次,给出灰度与RGB颜色的关系公式:gray = 0.3*r+0.6*g+0.1*b。有了这些的知识,现在给出用上述c++位图读入函数读入的位图的灰度数组转换函数;

 

//------------------------------------------------

//将一幅图片的像素区,转换为灰度值数组

//------------------------------------------------

template<typename T>

bool tmBmp::tranToArray(T * pA)

{

    DWORD i,j;

WORD  k,index=0,pixelPerB=0;

    if (bmpInfoH.biBitCount==1)

    {

        pixelPerB=8;

 

        for(i=0;i<bmpInfoH.biHeight;i++)

            for(j=0;j<storeWidth;j++ )

            {

                k=0;

                while(k<=7)

                {

                    index=(grasp(i,j)>>(7-k))&1;

                    fetch(pA,bmpInfoH.biWidth,i,j*pixelPerB+k)=

                    (T)(0.3*bmpRGB[index].rgbRed

                      +0.6*bmpRGB[index].rgbGreen

                      +0.1*bmpRGB[index].rgbBlue);

                    k++;

                }

            }

    }

    else if (bmpInfoH.biBitCount==4)

    {

        pixelPerB=2;

 

        for(i=0;i<bmpInfoH.biHeight;i++)

            for(j=0;j<storeWidth;j++)

            {

                k=0;

                while(k<=1&&(j*pixelPerB+k)<bmpInfoH.biWidth)

                {

                    index=(grasp(i,j)>>(1-k)*4)&15;

                    fetch(pA,bmpInfoH.biWidth,i,j*pixelPerB+k)=

                    (T)(0.3*bmpRGB[index].rgbRed

                    +0.6*bmpRGB[index].rgbGreen

                    +0.1*bmpRGB[index].rgbBlue);

                    k++;

                }

            }

    }

    else if (bmpInfoH.biBitCount==8)

    {

        for(i=0;i<bmpInfoH.biHeight;i++)

            for(j=0;j<storeWidth;j++)

            {

                index=grasp(i,j);

                fetch(pA,bmpInfoH.biWidth,i,j)=

                (T)(0.3*bmpRGB[index].rgbRed

                +0.6*bmpRGB[index].rgbGreen

                +0.1*bmpRGB[index].rgbBlue);

            }

    }

    else if ( (bmpInfoH.biBitCount==16)&& (BI_RGB == bmpInfoH.biCompression))

    {

        BYTE r,g,b;

        for (i = 0;i < bmpInfoH.biHeight; i++)

            for(j = 0, k = 0; j < bmpInfoH.biWidth; j++, k += 2)

            {

                b= (grasp(i,k)&0x1F)*8;

                g= (grasp(i,k)>>5)*8 + ((grasp(i,k+1)<<5)>>2)*8;

                r= (grasp(i,k+1)>>3)*8;

               

                fetch(pA,bmpInfoH.biWidth,i,j)= (T)(0.3*r + 0.6*g + 0.1*b);

            }

    }

    else if (bmpInfoH.biBitCount==24)

    {

        for (i = 0;i < bmpInfoH.biHeight; i++)

            for(j = 0, k = 0; j < bmpInfoH.biWidth; j++ , k += 3)

            {

                fetch(pA,bmpInfoH.biWidth,i,j)=

                (T)(0.1*grasp(i,k) + 0.6*grasp(i,k+1) + 0.3*grasp(i,k+2) );

            }

    }

    else if ( (bmpInfoH.biBitCount==32)&& (BI_RGB == bmpInfoH.biCompression))

    {

        for (i = 0;i < bmpInfoH.biHeight; i++)

            for(j = 0, k = 0; j < bmpInfoH.biWidth; j++, k += 4)

            {

                fetch(pA,bmpInfoH.biWidth,i,j)=

                (T)(0.1*grasp(i,k+1) + 0.6*grasp(i,k+2) + 0.3*grasp(i,k+3));

            }

    }

    else return false;

    return true;

}

 

 

四、压缩编码解析

 

BI_RLE8(8位位图的压缩)

在这种情况下BITMAPINFOHEADER结构中的biCompression设置为BI_RLE8,.使用256色位图行程编码格式将位图进行压缩。这种压缩方式包括绝对方式和编码方式。

 

编码方式:

在此方式下每两个字节组成一个信息单元。第一个字节给出其后面相连的象素的个数。第二个字节给出这些象素使用的颜色索引表中的索引。例如:信息单元03 04,03表示其后的象素个数是3个,04表示这些象素使用的是颜色索引表中的第五项的值。压缩数据展开后就是04 04 04 .同理04 05 可以展开为05 05 05 05.

信息单元的第一个字节也可以是00,这种情况下信息单元并不表示数据单元,而是表示一些特殊的含义。这些含义通常由信息单元的第二个字节的值来描述。这些值在0x00到0x02之间。具体含义如下:00 本行结束 ;01 位图结束 ;02 象素位置增量,表示紧跟在这个字节后面的信息单元里的两个字节中所包含的无符号值指定了下个象素相对于当前象素的水平和垂直偏移量(相对方式的意义体现在此)。例如:00 02 06 08表示的含义是下一个象素的位值是从当前位置向右移动5个象素,向下移动8个象素(不是字节)。

 

绝对方式:

绝对方式的标志是第一个字节是0,第二个字节是0x03到0xff之间的值。第二个字节的值表示跟随其后面的象素的字节数目。每个字节都包含一个象素的颜色索引。 每个行程编码都必须补齐到字的边界。

 

BI_RLE4(4位位图压缩)

这是BITMAPINFOHEADER的biCompression设置为BI_RLE4,使用16位行程编码格式进行位图压缩。压缩方式也包括编码方式和绝对方式。

 

编码方式:

4位压缩的编码方式跟8位的编码的压缩方式没有什么区别。每个信息单元也是由两个字节表示,第一个字节表示其后面所跟随的象素的个数。第二个字节表示象素在颜色索引表中的索引。这个字节又分为上下两个部分。第一个象素用上半部分指定的颜色表中的颜色画出。第二个象素用下半部分的颜色画出。第三个象素用下一个字节 的上半部分画出,依次类推。它只能压缩的颜色数不超过16的图像。因而这种压缩应用范围有限。

其余的跟BI_RLE8一样。

这个宏都是MFC的调试宏.

ASSERT_VALID宏用来在运行时检查一个对象的内部合法性,比如说现在有一个学生对象,我们知道每个学生的年龄一定大于零,若年龄小于零,则该学生对象肯定有问题。

 

事实上,ASSERT_VALID宏就是转化为对象的成员函数AssertValid()的调用,只是这种方法更安全。它的参数是一个对象指针,通过这个指针来调用它的AssertValid()成员函数。

与此相配套,每当我们创建从Cobject类继承而来的一个新的类时,我们可以重载该成员函数,以执行特定的合法性检查


ASSERT_VALID
强制调用参数对象(必须是CObject或CObject*)的AssertValid函数,该函数实现对象的内部一致性检查,当你创建一个可重用类时,应该重载这个函数(VC中缺省已经重载了该函数),你可以在该函数中进行必要的检查工作.
ASSERT宏用于确保参数内的表达式正确,如果表达式为false,则会显示一个消息对话框,其中有源文件的名字和当前行号,用户可以选择中断程序或进行调试.这个宏通常用于校验参数和返回值.
以上两个宏均只在Debug版本中有效,与ASSERT相对应的是VERIFY.VERIFY宏在Debug版本中与ASSERT相同,在Release版本中仅执行参数表达式,不进行校验.

ASSERT和 ASSERT_VALID
都是用于Debug的,当括号中的表达式为FALSE时,会弹出对话框通知,
你可以自己加上一句ASSERT(FALSE),看看执行时有什么东东。

ASSERT_VALID用于对C++对象或指针进行有效性判断,如果出错,结果和ASSERT一样。

注意:二者都只能在MFC工程里用

调色板原理 &编程

 

逻辑调色板结构LOGPALETTE,该结构定义如下:
  typedef struct tagLOGPALETTE
  {
    WORD palVersion;  //调色板的板本号,应该指定该值为0x300;
    WORD palNumEntries;//调色板中的表项数,对于灰度图像该值为256;
    PALETEENTRY palPalEntry[1];//调色板中的颜色表项,由于该表项的数目不一定,所以这里数组长度定义为1,灰度图像对应的该数组的长度为                                                          256;
  } LOGPALETTE;


  颜色表项结构PALETTEENTRY定义了调色板中的每一个颜色表项的颜色和使用方式,定义如下:
  typedef struct tagPALETTEENTRY
  {
    BYTE peRed; //R分量值;
    BYTE peGreen; //G分量值;
    BYTE peBlue; //B分量值;
    BYTE peFlags; // 该颜色被使用的方式,一般情况下设为"0";
  }PALETTEENTRY;

  Windows系统使用调色板管理器来管理与调色板有关的操作,通常活动窗口的调色板即是当前系统调色板,所有的非活动窗口都必须按照此系统调色板来显示自己的颜色,此时调色板管理器将自动的用系统调色板中的最近似颜色来映射相应的显示颜色。如果窗口或应用程序按自己的调色板显示颜色,就必须将自己的调色板载入到系统调色板中

这种操作叫作实现调色板,实现调色板包括两个步骤:

1.  首先将调色板选择到设备上下文中,可以通过CDC::SelectPalette()选入设备上下文

2.  然后在设备上下文中实现调色板,    可以通过 CDC::RealizePalette()实现设备调色板。

 

在实现调色板的过程中,通过在框架类中处理Windows定义的消息WM_QUERYNEWPALETTE 、WM_PALETTECHANGED及视图类中处理自定义消息WM_DOREALIZE(该消息在主框架窗口定义如下:#defineWM_REALIZEPAL (WM_USER+101))来实现调色板的操作。当系统需要处理调色板的变化时,将向程序的主窗口发送WM_QUERYNEWPALETTE 、WM_PALETTECHANGED,例如当某一窗口即将激活时,主框架窗口将收到WM_QUERYNEWPALETTE消息,通知该窗口将要收到输入焦点,给它一次机会实现其自身的逻辑调色板;当系统调色板改变后,主框架窗口将收到WM_PALETTECHANGED消息,通知其它窗口系统调色板已经改变,此时每一窗口都应该实现其逻辑调色板,重画客户区。

  由于上述的调色板变更消息是发往主框架窗口的,所以我们只能在主窗口中响应这两个消息,然后由主框架窗口通知各个视窗,使得程序激活时能自动装载自己的调色板。我们定义的用户消息WM_REALIZEPAL用于主框架窗口通知视窗它已经收到调色板变更消息,视窗应该协调其调色板。下面我们给出了各个消息的响应处理函数的具体实现代码和注释:

//////////////////////////////////////////////////////////
void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd)
{  //总实现活动视的调色板
  CMDIFrameWnd::OnPaletteChanged(pFocusWnd);
  CMDIChildWnd* pMDIChildWnd = MDIGetActive();//得到活动的子窗口指针;
  if (pMDIChildWnd == NULL)
      return
  CView* pView = pMDIChildWnd->GetActiveView();//得到视图的指针;
  ASSERT(pView != NULL);
  SendMessageToDescendants(WM_DOREALIZE,(WPARAM)pView->m_hWnd); //通知所有子窗口系统调色板已改变
}
////////////////////////////////////////////////
BOOL CMainFrame::OnQueryNewPalette()//提供实现系统调色板的机会
{
    // 实现活动视的调色板
  CMDIChildWnd* pMDIChildWnd = MDIGetActive();//得到活动的子窗口指针;
  if (pMDIChildWnd == NULL)
        return FALSE;//no active MDI child frame (nonew palette)
  CView* pView = pMDIChildWnd->GetActiveView();//得到活动子窗口的视图指针;
        ASSERT(pView != NULL);

  pView->SendMessage(WM_DOREALIZE,(WPARAM)pView->m_hWnd);//通知活动视图实现系统调色板
    return TRUE;
}
/////////////////////////////////////////////////
BOOL CDibView::OnDoRealize(WPARAM wParam, LPARAM)//实现系统调色板
{
  ASSERT(wParam != NULL);
  CDibDoc* pDoc = GetDocument();
  if (pDoc->m_hDIB == NULL)
      return FALSE; // must be a new document
  CPalette* pPal = pDoc->m_palDIB;
//调色板的颜色表数据在InitDIBData()函数中实现
  if (pPal != NULL)
  {
    CMainFrame* pAppFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd;//得到程序的主框架指针;
    ASSERT_KINDOF(CMainFrame, pAppFrame);
    CClientDC appDC(pAppFrame);//获取主框架的设备上下文;
    CPalette* oldPalette = appDC.SelectPalette(pPal,((HWND)wParam) != m_hWnd);
    if (oldPalette != NULL)
    {
      UINT nColorsChanged = appDC.RealizePalette();//实现系统调色板
      if (nColorsChanged > 0)
        pDoc->UpdateAllViews(NULL);//更新视图
      appDC.SelectPalette(oldPalette, TRUE); //将原系统调色板置为背景调色板
  }
  else
  {
      TRACE0("\\tSelectPalette failed in");
  }
  return TRUE;
}

  注:在调用API函数显示位图时,不要忘记设置逻辑调色板,即"背景"调色板,否则位图将无法正确显示,读者可以从后面的显示部分的实现看出我们在显示时实现了逻辑调色板。上述的处理相对来说比较繁琐复杂,可能对于初学者来说也比较难于理解,所以如果我们的程序仅仅限于处理灰度图象,可以采用另外一种相对简单的办法,即在文档类的初始化阶段定义一个灰度调色板,然后在设备上下文中实现它,这样作的好处是在度取灰度位图时可以不再考虑文件中的颜色表信息,提高了文件读取速度,笔者在开发一个基于机器视觉的项目时采用的就是这种方法,取的了比较满意的效果。首先定义一个指向逻辑颜色表结构 LOGPALETTE的指针pPal,填充该指针,然后将该指针与调色板指针联系起来,该方法的具体实现如下:

/////////////////////////////////////////////////////////
  CDibDoc::CDibDoc()
  {
    LOGPALETTE *Pal;
    Pal=new LOGPALETTE;
    m_palDIB=new Cpalette;
    pPal->palVersion=0x300;
    pPal->palNumEntries=256;
    for(int i=0;i<256;i++)
    {//每个颜色表项的R、G、B值相等,并且各个值从"0"到"255"序列展开;
      Pal->palPalentry[i].peRed=i;
      pPal->palPalentry[i].peGreen=i;
      pPal->palPalentry[i].peBlue=i;
      pPal->palPalentry[i].peFlags=0;
    }
      

  m_palDIB->CreatePalette(pPal);
  }

 

 

2.  调色板的原理

 PC机上显示的图象是由一个个像素组成的,每个像素都有自己的颜色属性。在PC的显示系统中,像素的颜色是基于RGB模型的,每一个像素的颜色由红(B)、绿(G)、蓝(B)三原色组合而成。每种原色用8位表示,这样一个的颜色就是24位的。以此推算,PC的SVGA适配器可以同时显示224约一千六百多万种颜色。24位的颜色通常被称作真彩色,用真彩色显示的图象可达到十分逼真的效果。但是,真彩色的显示需要大量的视频内存,一幅640×480的真彩色图象需要约1MB的视频内存。由于数据量大增,显示真彩色会使系统的整体性能迅速下降。

 

如图为一个256色显示模式中的调色板的工作原理

 

 图11.1调色板工作原理

 

为了解决这个问题,计算机使用调色板来限制颜色的数目。调色板实际上是一个有256个表项的RGB颜色表,颜色表的每项是一个24位的RGB颜色值。使用调色板时,在视频内存中存储的不是的24位颜色值,而是调色板的4位或8位的索引。这样一来,显示器可同时显示的颜色被限制在256色以内,对系统资源的耗费大大降低了(不同时刻可以采用不同的调色板,因此可以扩展总共可以显示的颜色)

 显示器可以被设置成16、256、64K、真彩色等显示模式,前两种模式需要调色板。在16或256色模式下,程序必须将想要显示的颜色正确地设置到调色板中,这样才能显示出预期的颜色。图11.1显示了调色板的工作原理。使用调色板的一个好处是不必改变视频内存中的值,只需改变调色板的颜色项就可快速地改变一幅图象的颜色或灰度。

  在DOS中,调色板的使用不会有什么问题。由于DOS是一个单任务操作系统,一次只能运行一个程序,因此程序可以独占调色板。在Windows环境下,情况就不那么简单了。Windows是一个多任务操作系统,可以同时运行多个程序。如果有几个程序都要设置调色板,就有可能产生冲突。为了避免这种冲突,Windows使用逻辑调色板来作为使用颜色的应用程序和系统调色板(物理调色板)之间的缓冲。 

 

 

 

图11.2 调色板的映射关系

 

在Windows中,应用程序是通过一个或多个逻辑调色板来使用系统调色板(物理调色板)。

在256色系统调色板中,Windows保留了20种颜色作为静态颜色,这些颜色用作显示Windows界面,应用程序一般不能改变。

缺省的系统调色板只包含这20种静态颜色,调色板的其它项为空。应用程序要想使用新的颜色,必须将包含有所需颜色的逻辑调色板实现到系统调色板中。在实现过程中

1. Windows首先将逻辑调色板中的项与系统调色板中的项作完全匹配,对于逻辑调色板中不能完全匹配的项,Windows将其加入到系统调色板的空白项中,系统调色板总共有236个空白项可供使用,

1. 若系统调色板已满,则Windows将逻辑调色板的剩余项匹配到系统调色板中尽可能接近的颜色上。

每个设备上下文都拥有一个逻辑调色板,缺省的逻辑调色板只有20种保留颜色,如果要使用新的颜色,则应该创建一个新的逻辑调色板并将其选入到设备上下文中。但光这样还不能使用新颜色,程序只有把设备上下文中的逻辑调色板实现到系统调色板中,新的颜色才能实现。在逻辑调色板被实现到系统调色板时,Windows会建立一个调色板映射表。当设备上下文用逻辑调色板中的颜色绘图时,GDI绘图函数会查询调色板映射表以把像素值从逻辑调色板的索引转换成系统调色板的索引,这样当像素被输出到视频内存中时就具有了正确的颜色值。图11.2说明了这种映射关系,从图中读者可以体会到逻辑调色板的缓冲作用。在该图中,GDI绘图函数使用逻辑调色板的索引1中的颜色来绘图,通过查询调色板映射表,得知系统调色板中的第23号索引与其完全匹配,这样实际输出到视频内存中的像素值是23。注意图中还演示了颜色的不完全匹配,即逻辑调色板中的索引15和系统调色板中的索引46。

每个要使用额外颜色的窗口都会实现自己的逻辑调色板,逻辑调色板中的每种颜色在系统调色板中都有相同或相近的匹配。调色板的实现优先权越高,匹配的精度也就越高。Windows规定,活动窗口的逻辑调色板(如果有的话)具有最高的实现优先权。这是因为活动窗口是当前与用户交互的窗口,应该保证其有最佳的颜色显示。非活动窗口的优先权是按Z顺序自上到下确定的(Z顺序就是重叠窗口的重叠顺序)。活动窗口有权将其逻辑调色板作为前景调色板实现,非活动窗口则只能实现背景调色板。

  提示:术语活动窗口(Active window)或前台窗口(Foreground window)是指当前与用户交互的窗口,活动窗口的顶端的标题条呈高亮显示,而非活动窗口的标题条则是灰色的。活动窗口肯定是一个顶层窗口(Top-level window),顶层窗口是指没有父窗口或父窗口是桌面窗口的窗口,这种窗口一般都有标题和边框,主要包括框架窗口和对话框。术语重叠窗口是指作为应用程序主窗口的窗口,我们可以把对话框看成是一种特殊的重叠式窗口。

 

3. 调色板的创建和实现

 

MFC的CPalette类对逻辑调色板进行了封装。该类的成员函数CreatePalette负责创建逻辑调色板,该函数的声明为:

BOOL CreatePalette( LPLOGPALETTElpLogPalette ); //成功则返回TRUE

参数lpLogPalette是一个指向LPLOGPALETTE结构的指针,LPLOGPALETTE结构描述了逻辑调色板的内容,该结构的定义为:

typedef struct tagLOGPALETTE {

  WORD palVersion; //Windows版本号,一般是0x300

  WORD palNumEntries; //调色板中颜色表项的数目

  PALETTEENTRY palPalEntry[1]; //每个表项的颜色和使用方法

} LOGPALETTE;

  结构中最重要的成员是PALETTEENTRY数组,数组项的数目由palNumEntries成员指定。PALETTEENTRY结构对调色板的某一个颜色表项进行了描述,该结构的定义为:

typedef struct tagPALETTEENTRY {

     BYTE peRed;  //红色的强度(0~255,下同)

     BYTE peGreen; //绿色的强度

     BYTE peBlue;  //蓝色的强度

     BYTE peFlags;

} PALETTEENTRY;

成员peFlags说明了颜色表项的使用方法,在一般应用时为NULL,若读者对peFlags的详细说明感兴趣,可以查看Visual C++的联机帮助。

可以看出,创建调色板的关键是在PALETTEENTRY数组中指定要使用的颜色。这些颜色可以是程序自己指定的特殊颜色,也可以从DIB位图中载入。逻辑调色板的大小可根据用户使用的颜色数来定,一般不能超过256个颜色表项。

  CreatePalette只是创建了逻辑调色板,此时调色板只是一张孤立的颜色表,还不能对系统产生影响。程序必需调用CDC::SelectPalette把逻辑调色板选入到要使用它的设备上下文中,然后调用CDC::RealizePalette把逻辑调色板实现到系统调色板中。函数的声明为:

CPalette* SelectPalette( CPalette*pPalette, BOOL bForceBackground );
  该函数把指定的调色板选择到设备上下文中。参数pPalette指向一个CPalette对象。参数bForceBackground如果是TRUE,那么被选择的调色板总是作为背景调色板使用,如果bForceBackground是FALSE并且设备上下文是附属于某个窗口的,那么当窗口是活动窗口或活动窗口的子窗口时,被选择的调色板将作为前景调色板实现,否则作为背景调色板实现。如果使用调色板的是一个内存设备上下文,则该参数被忽略。函数返回设备上下文原来使用的调色板,若出错则返回NULL。

UINT RealizePalette( );
该函数把设备上下文中的逻辑调色板实现到系统调色板中。函数的返回值表明调色板映射表中有多少项被改变了。

如果某一个窗口要显示特殊的颜色,那么一般应该在处理WM_PAINT消息时实现自己的逻辑调色板。也就是说,在OnPaint或OnDraw函数中重绘以前,要调用SelectPalette和RealizePalette。如果窗口显示的颜色比较重要,则在调用SelectPalette时应该指定bForceBackground参数为FALSE。

前景调色板具有使用颜色的最高优先级,它有无条件占用系统调色板(20种保留颜色除外)的权力,也就是说,如果需要,前景调色板将覆盖系统调色板的236个表项,而不管这些表项是否正被别的窗口使用。背景调色板则无权破坏系统调色板中的已使用项。

 

请读者注意,前景调色板应该是唯一

如果一个活动窗口同时要实现几个逻辑调色板,那么只能有一个调色板作为前景调色板实现,也即在调用CDC::SelectPalette时只能有一个bForceBackground被指定为FALSE,其它的bForceBackground必需为TRUE。

通常是把具有输入焦点的窗口的调色板作为前景调色板实现,其它窗口只能使用背景调色板。

如果活动窗口的子窗口全都使用前景调色板,则会导致程序的死循环。

  提示:请读者注意区分活动窗口和有输入焦点的窗口。有输入焦点的窗口要么是活动窗口本身,要么是活动窗口的子窗口。也就是说,活动窗口不一定具有输入焦点,当活动窗口的子窗口获得输入焦点时,活动窗口就会失去输入焦点。

4. 使用颜色的三种方法

 

 在调用GDI函数绘图时,可以用不同的方法来选择颜色。Windows用COLORREF数据类型来表示颜色,COLORREF型值的长度是4字节,其中最高位字节可以取三种不同的值,分别对应三种使用颜色的方法。表11.1列出了这些不同的取值及其含义。

 COLORREF型值的最高位字节的含义    

取值

含义

0x00

指定RGB引用。此时三个低位字节含有红、绿、蓝色的强度,Windows将抖动20种保留的颜色来匹配指定的颜色,而不管程序是否实现了自己的调色板。

0x01

指定调色板索引引用。此时最低位字节含有逻辑调色板的索引,Windows根据该索引在逻辑调色板中找到所需的颜色。

0x02

指定调色板RGB引用。此时三个低位字节含有红、绿、蓝色的强度,Windows会在逻辑调色板中找到最匹配的颜色。

 

为了方便用户的使用,Windows提供了三个宏来构建三种不同的COLORREF数据,它们是:

COLORREF RGB(BYTE bRed,BYTE bGreen,BYTEbBlue); //RGB引用

COLORREF PALETTEINDEX(WORDwPaletteIndex);    //调色板索引引用

COLORREF PALETTERGB(BYTE bRed,BYTEbGreen, BYTE bBlue);    //调色板RGB引用

 

例如,我们可以用上述三种方法来指定刷子的颜色:

(1). 调用系统调色板中的红色建立一个刷子:

  CBrush brush;

  brush.CreateSolidBrush(RGB(255,0,0));

  pDC->SelectObject(&brush);

(2). 调用逻辑调色板的索引2中的颜色来创建一个刷子:

  pDC->SelectPalette(&m_Palette,FALSE);

  pDC->RealizePalette( );

  CBrush brush;

  brush.CreateSolidBrush(PALETTEINDEX(2));

  pDC->SelectObject(&brush);

(3).调用逻辑调色板中最匹配的深灰色来创建一个刷子:

  pDC->SelectPalette(&m_Palette,FALSE);

  pDC->RealizePalette( );

  CBrush brush;

  brush.CreateSolidBrush(PALETTERGB(20,20,20));

  pDC->SelectObject(&brush);

 

5. 与系统调色板有关的消息

 

 为了协调各个窗口对系统调色板的使用,Windows在必要的时侯会向顶层窗口和重叠窗口发送消息WM_QUERYNEWPALETTE和WM_PALETTECHANGED。

 当某一顶层或重叠窗口(如主框架窗口)被激活时,会收到WM_QUERYNEWPALETTE消息,在窗口创建之初也会收到该消息,该消息先于WM_PAINT消息到达窗口。如果活动窗口要使用特殊的颜色,则在收到该消息时应该实现自己的逻辑调色板并重绘窗口。如果窗口实现了逻辑调色板,那么WM_QUERYNEWPALETTE消息的处理函数应返回TRUE。通常窗口在收到该消息后应该为有输入焦点的窗口(如视图)实现前景调色板,但如果程序觉得它显示的颜色并不重要,那么在收到该消息后可以把逻辑调色板作为背景调色板实现(指定CDC::SelectPalette函数的bForceBackground参数为TRUE),这样程序就失去了使用系统调色板的最高优先权。

 当活动窗口实现其前景调色板并改变了系统调色板时,Windows会向包括活动窗口在内的所有的顶层窗口和重叠窗口发送WM_PALETTECHANGED消息,在该消息的wParam参数中包含了改变系统调色板的窗口的句柄。其它窗口如果使用了自己的逻辑调色板,那么应该重新实现其逻辑调色板,并重绘窗口。这是因为系统调色板已经被改变了,必需重新建立调色板映射表并重绘,否则可能会显示错误的颜色。当然,非活动窗口只能使用背景调色板,所以显示的颜色肯定没有在前台的时侯好。要注意只有在活动窗口实现了前景调色板且改变了系统调色板时,才会产生WM_PALETTECHANGED消息。也就是说,如果窗口在调用CDC::SelectPalette时指定bForceBackground参数为TRUE,那么是不会产生WM_PALETTECHANGED消息。

 总之,WM_QUERYNEWPALETTE消息为活动窗口提供了实现前景调色板的机会,而WM_PALETTECHANGED消息为窗口提供了适应系统调色板变化的机会。

  需要指出的是,子窗口是收不到与调色板有关的消息的。因此,如果子窗口(如视图)要使用自己的逻辑调色板,那么顶层窗口或重叠窗口应该及时通知子窗口与调色板有关的消息。

 

6. 具体实例

  现在让我们来看一个使用调色板的演示程序。该程序名为TestPal,如图11.3所示,该程序显示了两组红色方块,每组方块都是16×16共256个。左边的这组方块是用逻辑调色板画的,红色的强度从0到255递增,作为对比,在右边用RGB引用画出了256个递增的红色方块。读者可以对比这两组方块的颜色质量,以体会调色板索引引用和RGB引用的区别。该程序也着重向读者演示了处理调色板消息的方法。

 

 

  首先,请读者用AppWizard建立一个名为TestPal的MFC单文挡应用程序。然后,用ClassWizard为CMainFrame类加入WM_QUERYNEWPALETTE和WM_PALETTECHANGED消息的处理函数,使用缺省的函数名。接着,在TestPal.h文件中类CTestPalApp的定义前加入下面一行:

#define WM_DOREALIZE WM_USER+200

当收到调色板消息时,主框架窗口会发送用户定义的WM_DOREALIZE消息通知视图。

最后,请读者按清单11.1和11.2修改程序。

清单11.1 CMainFrame类的部分代码

BOOL CMainFrame::OnQueryNewPalette()

{

// TODO: Add your message handler code hereand/or call default

GetActiveView()->SendMessage(WM_DOREALIZE);

return TRUE; //返回TRUE表明实现了逻辑调色板

}

void CMainFrame::OnPaletteChanged(CWnd*pFocusWnd)

{

CFrameWnd::OnPaletteChanged(pFocusWnd);

// TODO: Add your message handler code here

if(GetActiveView()!=pFocusWnd)

GetActiveView()->SendMessage(WM_DOREALIZE);

}

清单11.2 CTestPalView类的部分代码

// TestPalView.h : interface of the CTestPalViewclass

class CTestPalView : public CView

{

. . .

protected:

CPalette m_Palette;

. . .

afx_msg LRESULT OnDoRealize(WPARAM wParam,LPARAM lParam);

DECLARE_MESSAGE_MAP()

};

// TestPalView.cpp : implementation of theCTestPalView class

BEGIN_MESSAGE_MAP(CTestPalView, CView)

. . .

ON_MESSAGE(WM_DOREALIZE, OnDoRealize)

END_MESSAGE_MAP()

CTestPalView::CTestPalView()

{

// TODO: add construction code here

LPLOGPALETTE pLogPal;

pLogPal=(LPLOGPALETTE)malloc(sizeof(LOGPALETTE)+

sizeof(PALETTEENTRY)*256);

pLogPal->palVersion=0x300;

pLogPal->palNumEntries=256;

for(int i=0;i<256;i++)

{

pLogPal->palPalEntry[i].peRed=i; //初始化为红色

pLogPal->palPalEntry[i].peGreen=0;

pLogPal->palPalEntry[i].peBlue=0;

pLogPal->palPalEntry[i].peFlags=0;

}

if(!m_Palette.CreatePalette(pLogPal))

AfxMessageBox("Can't createpalette!");

}

void CTestPalView::OnDraw(CDC* pDC)

{

CTestPalDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

// TODO: add draw code for native data here

CBrush brush,*pOldBrush;

int x,y,i;

pDC->SelectPalette(&m_Palette,FALSE);

pDC->RealizePalette();

pDC->SelectStockObject(BLACK_PEN);

for(i=0;i<256;i++)

{

x=(i%16)*16;

y=(i/16)*16;

brush.CreateSolidBrush(PALETTEINDEX(i)); //调色板索引引用

pOldBrush=pDC->SelectObject(&brush);

pDC->Rectangle(x,y,x+16,y+16);

pDC->SelectObject(pOldBrush);

brush.DeleteObject();

}

for(i=0;i<256;i++)

{

x=(i%16)*16+300;

y=(i/16)*16;

brush.CreateSolidBrush(RGB(i,0,0)); //RGB引用

pOldBrush=pDC->SelectObject(&brush);

pDC->Rectangle(x,y,x+16,y+16);

pDC->SelectObject(pOldBrush);

brush.DeleteObject();

}

}

LRESULT CTestPalView::OnDoRealize(WPARAMwParam, LPARAM)

{

CClientDC dc(this);

dc.SelectPalette(&m_Palette,FALSE);

if(dc.RealizePalette()) //若调色板映射被改变则刷新视图

GetDocument()->UpdateAllViews(NULL);

return 0L;

}

  在CTestPalView的构造函数中创建了一个含有256个递增红色的逻辑调色板。

  当变为活动窗口以及窗口创建时,TestPal程序的主框架窗口都会收到WM_QUERYNEWPALETTE消息,该消息的处理函数OnQueryNewPalette负责发送WM_DOREALIZE消息通知视图, 并返回TRUE以表明活动窗口实现了逻辑调色板。WM_DOREALIZE消息的处理函数CTestPalView::OnDoRealize为视图实现一个前景调色板,该函数中有一个判断语句可提高程序运行的效率:如果CDC::RealizePalette返回值大于零,则说明调色板映射表发生了变化,此时必须刷新视图,否则制图中的颜色将失真。如果RealizePalette返回零则说明调色板映射没有变化,这时就没有必要刷新视图。

  无论是TestPal还是别的应用程序在实现前景调色板并改变了系统调色板时,TestPal程序的主框架窗口都会收到WM_PALETTECHANGED消息。请注意该消息的处理函数CMainFrame::OnPaletteChanged有一个pFocusWnd参数,该参数表明是哪一个窗口改变了系统调色板。函数用pFocusWnd来判断,如果是别的应用程序实现了前景调色板,则通知视图调用OnDoRealize实现其逻辑调色板,注意虽然CDC::SelectPalette的bForceBackground参数是FALSE,但这时视图的逻辑调色板是作为背景调色板实现的。如果是TestPal自己的视图实现了前景调色板,则没有必要调用OnDoRealize。

  请读者将Windows当前的显示模式设置为256色,然后编译并运行TestPal,对比一下RGB引用与调色板索引引用的效果,读者不难发现左边用调色板索引引用输出的颜色比右边好的多。通过该程序我们可以看出,即使在系统调色板中已实现了丰富的红色的情况下,RGB引用得到的红色仍然是20种保留颜色的抖动色。

  读者可以打开Windows的画笔程序,并在该程序中打开一幅256色的位图(如Windows目录下的Forest.bmp)。在画笔和TestPal程序之间来回切换,读者可以看到,由于两个应用程序都正确的处理了调色板消息,在前台的应用程序总是具有最好的颜色显示,而后台程序的颜色虽然有些失真,但还比较令人满意。

  需要指出的是,TestPal程序只使用了一个逻辑调色板,所以它处理调色板消息的方法比较简单。如果程序要用到多个逻辑调色板,那么就需要采取一些新措施来保证只有一个逻辑调色板作为前景调色板使用。在11.4节读者可以看到使用多个逻辑调色板时的处理方法

 

 

 

0 0
原创粉丝点击