网络上较多的是24为转8位的,因为24位转8位正好是将一个RGB值转为一个字节进行存储,所以比较好转。
24位转一位位图,与其逻辑相似,但是有一定区别。
1、取得24位图的3个字节的RGB值,按照一定方式转为1位二进制值。转换的方式有很多
/*
这里共有三种灰度的计算方法,这个可以随机取
1、每个颜色值按比例取值组为新的灰度值
2、计算点之间的距离,取较近的一个
3、除了纯白的(全是255的),其余全部认为是黑色点(0)
*/
//gray=(299*r+587*g+114*b)/1000;
//gray = (BYTE)( ( 256 * r) >> 8 ); // 24位转8位核心算法
//int iTo0 = (r-0)*(r-0)+(g-0)*(g-0)+(b-0)*(b-0);
//int iTo1 = (r-255)*(r-255)+(g-255)*(g-255)+(b-255)*(b-255);
//gray = iTo1 > iTo0 ? 0x00 : 0xFF;
if(255 == b && 255 == g && 255 == r)
{
gray = 0xff;
}
else
{
gray = 0x00;
}
这三种都是可以的。我的代码中采取第三种
2、补零的问题,bmp图像要求,每一行的字节数为4的整数倍,所以不管24位图还是1位图,都要注意。尤其是读取24位图的RGB数据时,要区分哪些是有效数据,哪些是为了字节对齐要求补足的无效数据,不然转换出来的图像会出现扭曲的情况。网络上有的绝大部分代码都没有注意到这种情况,所以一旦当图片的中存在补足的数据后就会出现图像转换后扭曲的情况。
将转换的代码封装成了一个函数,如下所示
void change(char *file)
{
FILE *opfile;
int iLineByte = 0;
int iSize = 0;
int iWidth = 0;
int iHeight = 0;
int iBitCount = 0;
BYTE bByte = 0;
int iOffset = 0;
int iOffsetBytes = 0;
unsigned char* pNewBuff = NULL;
if((opfile = fopen(file,"rb")) == NULL)
{
printf("File can not open!\n");
exit(1);
}
FILE *wrfile;
char sFileName[256] = {0};
sprintf(sFileName, "%s/data/%s", OSL_GetAppPath(), "QRCode.bmp");
if((wrfile = fopen(sFileName,"wb")) == NULL)
{
printf("File can not open!\n");
exit(1);
}
BITMAPFILEHEADER fileHead;
fread(&fileHead, sizeof(BITMAPFILEHEADER), 1, opfile);
BITMAPINFOHEADER bitmapinfoHead;
fread(&bitmapinfoHead, sizeof(BITMAPINFOHEADER), 1, opfile);
//计算1位图所占的总字节空间
iWidth = bitmapinfoHead.biWidth ;
iHeight = bitmapinfoHead.biHeight ;
iBitCount = bitmapinfoHead.biBitCount ;
iLineByte = (iWidth *iBitCount/8+3)/4*4;
iSize = (iLineByte*iHeight)/3;
iOffset = fileHead.bfOffBits ;
iOffsetBytes = iLineByte - (iWidth*iBitCount/8);
if(0 != ((iLineByte*iHeight)%3))
{
iSize = iSize + 1;
}
pNewBuff = new unsigned char[iSize];
COLORREF rgb;
UINT pitchcount;
BYTE r, g, b;
BYTE gray;
BYTE writein = 0;
int k;
bitmapinfoHead.biBitCount = 1;
bitmapinfoHead.biClrUsed = 0;
fileHead.bfOffBits = 54 + 2*sizeof(RGBQUAD);
bitmapinfoHead.biSizeImage = (((bitmapinfoHead.biWidth*bitmapinfoHead.biBitCount)+31)/32)*abs(bitmapinfoHead.biHeight)*4;
fileHead.bfSize = fileHead.bfOffBits + bitmapinfoHead.biSizeImage;
fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, wrfile);
fwrite(&bitmapinfoHead, sizeof(BITMAPINFOHEADER), 1, wrfile);
wrfile = SetRGBQUAD(wrfile);
fseek(opfile,iOffset,0);
for(int i = 0; i < abs(bitmapinfoHead.biHeight); i++)
{
pitchcount = 0;
k = 0;
for (int j = 0; j < bitmapinfoHead.biWidth; j++)
{
fread(&rgb, 3, 1, opfile);
if(feof(opfile))
{
break;
}
// rgb: 0x00bbggrr
b = GetBValue(rgb);
g = GetGValue(rgb);
r = GetRValue(rgb);
/*
这里共有三种灰度的计算方法,这个可以随机取
1、每个颜色值按比例取值组为新的灰度值
2、计算点之间的距离,取较近的一个
3、除了纯白的(全是255的),其余全部认为是黑色点(0)
*/
//gray=(299*r+587*g+114*b)/1000;
//gray = (BYTE)( ( 256 * r) >> 8 ); // 24位转8位核心算法
//int iTo0 = (r-0)*(r-0)+(g-0)*(g-0)+(b-0)*(b-0);
//int iTo1 = (r-255)*(r-255)+(g-255)*(g-255)+(b-255)*(b-255);
//gray = iTo1 > iTo0 ? 0x00 : 0xFF;
if(255 == b && 255 == g && 255 == r)
{
gray = 0xff;
}
else
{
gray = 0x00;
}
/*
note by wangwei 20141029
24位转一位的比例是1:24,每三个字节组成一位,每24个字节组成一个新图的字节
*/
bByte = (bByte<<1)| (gray>>7);
k++;
if(8 == k)
{
fwrite(&bByte, sizeof(char), 1, wrfile);
bByte = 0;
k = 0;
pitchcount++;
}
}
/*
add by wangwei 20141029
这句代码很重要,如果没有的话,如果缩放0.25这类的比例,会出现图像走样的情况
因为原图也是存在补足字节的情况的,也是要求每行的字节数是4的整数倍数的,而上面一个循环结束,就是实际像素的结束,
这时候如果不补足的话,是另起一行的,实际为了补足为4的倍数,会补足一堆无意义字节,而这类补足的字节是不能作为新图的像素依据的,
所以需要调过这类无意义字节
*/
fseek(opfile,iOffsetBytes,SEEK_CUR);
if(0 != k)
{
bByte = bByte <<(8-k);
fwrite(&bByte,sizeof(char),1,wrfile);
k = 0;
bByte = 0;
pitchcount++;
}
if(0 != (pitchcount%4))
{
k = pitchcount%4;
for(int iIndex = 0;iIndex<(4-k);iIndex++)
{
fwrite(&bByte,sizeof(char),1,wrfile);
pitchcount = pitchcount + 1;
}
}
}
fclose(opfile);
fclose(wrfile);
memset(pNewBuff,0x00,sizeof(pNewBuff));
delete []pNewBuff;
pNewBuff = NULL;
}
代码中部分变量是为了测试用才定义的,并不是代码完全不能更改的。