⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ddb.cpp

📁 《Visual C++数字图像与图形处理》一书中编译通过的图像显示程序
💻 CPP
📖 第 1 页 / 共 3 页
字号:
			{	
				//对于16位和32位位图, 由于数据需要重组, 所以需要重新分配内存

				//指向Dib实际像素数据的指针, 
				BYTE* pbyDibBits = new BYTE[dwDibBitsSize]; 
				if (pbyDibBits == NULL)	return; 
				
				//数据索引基数
				DWORD dwBaseIndex = y * m_nDdbWidthBytes + ((m_nBitCount == 16) ? (x + x) : (4 * x)); 
				DWORD dwIndexDib = (h - 1) * dwSaveWidthBytes; 
				//离析像素数据, 并将其写入内存, 从上往下读
				for(LONG i = 0;  i < h; i++)
				{
				
					//指向Ddb内存数据的指针(行)
					BYTE* pbyDdbData = m_pDdbData + dwBaseIndex; 

					//指向Dib内存数据的指针(行)
					BYTE* pbyDibData = pbyDibBits + dwIndexDib; 

					for(LONG j = 0; j < w; j++)
					{
						
						//处理16位
						if(m_nBitCount == 16)
						{
							//记录一个像素点的24位颜色值(16位和32位模式均被转化为24位)
							BYTE* pbyRGB = new BYTE[3]; 
							WORD* pwDdbData_16 = (WORD*)(pbyDdbData++); 
							GetRGB16(pbyRGB, pwDdbData_16); 
							pbyDdbData++; 
							*pbyDibData++ =  pbyRGB[0]; 	//b
							*pbyDibData++ =  pbyRGB[1];  //g
							*pbyDibData++ =  pbyRGB[2]; 	//r
							delete[] pbyRGB; 
						}

						//处理32位
						if(m_nBitCount == 32)
						{
							*pbyDibData++ = *pbyDdbData++; 	//蓝色
							*pbyDibData++ = *pbyDdbData++; 	//绿色
							*pbyDibData++ = *pbyDdbData++; 	//红色
							pbyDdbData++; 
						}
			
					}//end j
					dwBaseIndex += m_nDdbWidthBytes; 
					dwIndexDib -= dwSaveWidthBytes; 
				}//end  i

				//将内存数据写入文件
				file.WriteHuge((LPSTR)pbyDibBits, dwDibBitsSize); 
				
				delete[] pbyDibBits; 
			}
			break; 
		
		}

		file.Close(); 	
	}//end try
	catch(CFileException* e)
	{
		e->Delete(); 
	}

	AfxGetApp()->EndWaitCursor(); 
}

//创建调色盘表项:其内容来自于系统调色盘
void CDdb::GetSystemPaletteEntries(RGBQUAD *pRGBQuad)
{
	ASSERT(pRGBQuad); 
	HDC hDC = ::GetDC(NULL); //If this value is NULL,  GetDC retrieves the device context for the entire screen. 

	PALETTEENTRY* paletteEntry = new PALETTEENTRY[256]; 

	//获取系统调色盘的信息.系统调色盘的改变由系统来完成, 这里不能改变系统调色盘.我们希望这个类不要干涉系统事务
	::GetSystemPaletteEntries(hDC, 0, 256, paletteEntry); 
	//::GetDIBColorTable(hDC, 0, 256, pRGBQuad); 
	
	for(int index = 0; index < 256; index++)
	{
		pRGBQuad[index].rgbRed = paletteEntry[index].peRed; 
		pRGBQuad[index].rgbGreen = paletteEntry[index].peGreen; 
		pRGBQuad[index].rgbBlue = paletteEntry[index].peBlue; 
		pRGBQuad[index].rgbReserved = 0; 
	}
	
	delete[] paletteEntry; 
	::ReleaseDC(NULL, hDC); 

}

//Windows 95 and Windows 98: A 5-5-5 16-bit image,  where the blue mask is 0x001F,  the green mask is 0x03E0,  and the red mask is 0x7C00;  and a 5-6-5 16-bit image,  where the blue mask is 0x001F,  the green mask is 0x07E0,  and the red mask is 0xF800.

//获取16位颜色的RGB值
void CDdb::GetRGB16(BYTE *pbyRGB,  WORD *pwColor)
{
	WORD* pwClr = pwColor; 

	//(WORD)颜色顺序:	红色--绿色--蓝色	(从左至右: 5-6-5)
	//字节顺序:蓝色--绿色--红色

	//蓝色--(WORD)低5位
	pbyRGB[0] = (BYTE) (((*pwClr) & 0x001F) << 3); 	//b
	//绿色--(WORD)中6位
	pbyRGB[1] = (BYTE) (((*pwClr) & 0x07E0) >> 3); 	//g		
	//红色--(WORD)高5位
	pbyRGB[2] = (BYTE) (((*pwClr) & 0xF800) >> 8); 	//r

	/*
	pbyRGB[0] = (BYTE) (((*pwClr) & 0x001F) << 3); 
	pbyRGB[1] = (BYTE) (((*pwClr) & 0x03E0) >> 2); 
	pbyRGB[2] = (BYTE) (((*pwClr) & 0x7C00) >> 7); 
	*/

}
 

//颜色量化操作
//颜色量化算法主有:
/*
//文献
Median Cut 
Paul Heckbert. Color image quantization for frame buffer display. ACM,  Computer Graphics,  pages 297--304,  1982. 
One of the many implementations is found in netpbm 

Variance-based method 
S. K. M. Wong S. J. Wan,  P. Prusinkewicz. Variance-based color image quantization for frame buffer display. Color Research and Application,  15(1):52--58,  February 1990. 
This algorithm is not used very often,  but produces better results than median cut. (Can be found in colorquant in Utah Raster Kit ) 

Octree 
W. Purgathofer M. Gervautz. A Simple Method for Color Quantization: Octree Quantization,  pages 219--231. Magenat-Thalmann and Thalmann,  1988. 
A very fast algorithm that produces good quality palettes. One of the implementations is found in quantize in ImageMagick. 
Kohonen Neural Network Quantization 
Anthony Dekker Kohonen neural networks for optimal colour quantization Network: Computation in Neural Systems Vol. 5 (1994) pp 351-367. 

Local K-means 
Oleg A. Verevka and John W. Buchanan Local K-means Algorithm for Color Image Quantization ,  To appear in Proceedings of GI 95 ,  Quebec City,  Quebec,  Canada. 
Oleg A. Verevka Color image quantization in windows systems with local K-means algorithm. Proceedings of VI Western Computer Graphics Symposium,  pages 74-79,  March 1995. 
*/
//我们这里采用最简单的一种:即将颜色立方体分成252份, 沿红色6份, 沿绿色7份, 
//沿蓝色6份.这种方法精确度高, 并且很快.

//pbDdbBits24----指向内存24位像素数据, 其宽度字节是2的倍数.
//nScanWidth-----图像子区域的宽度, 即像素个数
//nScanHeight----图像子区域的高度.
//lpbyDdbBits8---量化后的8位索引数据
//pPalette-------对应的调色盘

//对颜色进行量化, 结果被保存在逻辑调色盘pPalette中, 同时将数据量化为8位数据, 并存放于lpbyDdbBits8之中
void CDdb::QuantizeColor(LPBYTE lpbyDdbBits24, int nScanWidth, int nScanHeight, LPBYTE lpbyDdbBits8, CPalette* pPalette)
{
	ASSERT(lpbyDdbBits24); 
	ASSERT(lpbyDdbBits8); 
	//第一步:对颜色分布进行统计:
	//COLORQUANTIZATION结构记录位于小颜色立方体中的像素个数, 颜色分量之和.
	typedef struct tagCOLORQUANTIZATION
	{
		DWORD dwRedSum; 			//红色总量
		DWORD dwGreenSum; 		//绿色总量
		DWORD dwBlueSum; 		//蓝色总量
		DWORD dwPixelCount; 		//进入一个Box的颜色个数
		BYTE  byFlag; 			//是否抛弃, 等于0则抛弃, 非0则不抛弃
	}COLORQUANTIZATION; 

	//分配内存, 必须初始化为0
	BYTE* pbyColorBox = new BYTE[252 * sizeof(COLORQUANTIZATION)]; 
	if (pbyColorBox == NULL)	return ; 
	memset(pbyColorBox,  0,  252 * sizeof(COLORQUANTIZATION)); 
	//指向COLORQUANTIZATION数据的指针
	COLORQUANTIZATION* pCQBox = (COLORQUANTIZATION*)pbyColorBox; 

	//每行的大小(字节)
	DWORD dwWidthBytes24 =  ((nScanWidth * 24 + 15) / 16) * 2; 
	DWORD dwWidthBytes8 = ((nScanWidth * 8 + 15) / 16) * 2; 

	//现在进行统计
	DWORD dwBaseIndex24 = 0; 
	DWORD dwBaseIndex8 = 0; 
	int i, j; 
	for(i = 0;  i < nScanHeight;  i++)
	{
		//指向Ddb内存数据的指针(行)
		BYTE* pbyDdb24 = lpbyDdbBits24 + dwBaseIndex24; 
		BYTE* pbyDdb8 = lpbyDdbBits8 + dwBaseIndex8; 

		for(j = 0;  j < nScanWidth;  j++)
		{
			//获取当前点的颜色分量

			BYTE byBlue = *(pbyDdb24++); 
			BYTE byGreen = *(pbyDdb24++); 
			BYTE byRed = *(pbyDdb24++); 

			//计算当前颜色在立方体中的位置:由颜色三分量的值决定, 因而这也是颜色量化映射关系.
			BYTE byIndexR = (byRed >= 210) ? 5 : (byRed / 42); 
			BYTE byIndexG = (byGreen >= 216) ? 6 : (byGreen / 36); 
			BYTE byIndexB = (byBlue >= 210) ? 5 : (byBlue / 42); 
			BYTE byInBox = 42 *  byIndexB + 6 * byIndexG + byIndexR; 
			
			(pCQBox + byInBox)->dwPixelCount++; 
			(pCQBox + byInBox)->dwRedSum += byRed; 
		    (pCQBox + byInBox)->dwGreenSum += byGreen; 
		    (pCQBox + byInBox)->dwBlueSum += byBlue; 

			*(pbyDdb8++) = byInBox; 
		}
		dwBaseIndex24 += dwWidthBytes24; 
		dwBaseIndex8 += dwWidthBytes8; 
	}

	//第二步选出发生频率最高的236种颜色
	//临时数组, 用于存放分布频数
	DWORD* pdwFrequency = new DWORD[252]; 

	for(i = 0;  i < 252;  i++)
	{
		(pCQBox + i)->byFlag = 1; // not discard
		pdwFrequency[i] = (pCQBox + i)->dwPixelCount; 
	}

	//找出出现频率最少的16种颜色, 并标记为0
	for(i = 0;  i < 16;  i++)
	{
		//最小值
		DWORD dwMin = pdwFrequency[0]; 
		int nIndexMin = 0; 
		for(j = 1;  j < 252; j++)
		{
			if( pdwFrequency[j] < dwMin)
			{
				dwMin = pdwFrequency[j]; 
				nIndexMin = j; 
			}
		}
		pdwFrequency[nIndexMin] = 100000000L; 
		(pCQBox + nIndexMin)->byFlag = 0;  //discard
	}
	delete[] pdwFrequency; 
	
	//第三步, 为LOGOALETTE分配内存, 并对其进行填充:
	HDC hDC = ::GetDC(NULL); //If this value is NULL,  GetDC retrieves the device context for the entire screen. 
		
	BYTE* pbyLogPalette = new BYTE[sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY)]; 
	if(pbyLogPalette == NULL)return; 
	memset(pbyLogPalette,  0 ,  (sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY))); 
	LOGPALETTE* pLogPalette = (LOGPALETTE*)pbyLogPalette; 

	pLogPalette->palVersion = 0x300; 
	pLogPalette->palNumEntries = 256; 
	//获取系统调色盘的信息.//
	::GetSystemPaletteEntries(hDC, 0, 256, pLogPalette->palPalEntry); 
	::ReleaseDC(NULL, hDC); 

	//第四步, 采用颜色量化结果改写位于中间的236项, 并建立映射表
	
	//将填入236项的顺序号
	int nIndexMid = 10; 

	//记录被抛弃的顺序
	int nIndexThrow = 0; 

	//保存被抛弃的项
	BYTE abyDiscard[16]; 

	//映射表
	int anMap[252]; 

	
	//从252种入选颜色中顺序填入236种颜色:
	for(i = 0; i < 252; i++)
	{
		if((pCQBox + i)->byFlag != 0)
		{
			DWORD dwNum = (pCQBox + i)->dwPixelCount; 
			if(dwNum == 0)dwNum = 1; 

			(pLogPalette->palPalEntry + nIndexMid)->peBlue = (BYTE)(((pCQBox + i)->dwBlueSum) / dwNum); 		
			(pLogPalette->palPalEntry + nIndexMid)->peGreen = (BYTE)(((pCQBox + i)->dwGreenSum) / dwNum); 
			(pLogPalette->palPalEntry + nIndexMid)->peRed = (BYTE)(((pCQBox + i)->dwRedSum) / dwNum); 
			(pLogPalette->palPalEntry + nIndexMid)->peFlags = PC_NOCOLLAPSE; 
			anMap[i] = nIndexMid; 
			nIndexMid++; 
		}
		else
		{
			abyDiscard[nIndexThrow] = i; 
			nIndexThrow++; 
		}
	}

	//第五步, 创建逻辑调色盘, 对于16种被抛弃的颜色向邻近颜色映射
	pPalette->CreatePalette(pLogPalette); 
	for(i = 0; i < 16; i++)
	{
		DWORD dwNum = (pCQBox + i)->dwPixelCount; 
		if(dwNum == 0)dwNum = 1; 

		BYTE byRed = (BYTE)(((pCQBox + abyDiscard[i])->dwRedSum) / dwNum); 
		BYTE byGreen = (BYTE)(((pCQBox + abyDiscard[i])->dwGreenSum) / dwNum); 
		BYTE byBlue = (BYTE)(((pCQBox + abyDiscard[i])->dwBlueSum) / dwNum); 
		
		anMap[abyDiscard[i]] = (BYTE)pPalette->GetNearestPaletteIndex(RGB(byRed, byGreen, byBlue)); 
	}

	//第六步, 对8位数据重新进行映射

	DWORD dwSize = dwWidthBytes8 * nScanHeight; 
	BYTE* pbyBits8 = lpbyDdbBits8; 
	while(dwSize--)
	{
		*pbyBits8 = anMap[*pbyBits8]; 
		pbyBits8++; 
	}

	delete[] pbyColorBox; 
	delete[] pbyLogPalette; 
}

//功能:	另存为8位位图
void CDdb::SaveAs256(const char *pszDibFileName, int x, int y, int nWidth, int nHeight)
{
	ASSERT(pszDibFileName); 
	if(m_nBitCount <= 8) 
	{
		Save(pszDibFileName, x, y, nWidth, nHeight); 
		return; 
	}

	//首先进行参数合法性检测
	if((x > m_nWidth - 1) || (y > nHeight - 1))
	{
		AfxMessageBox("Cross the border!"); 
		return; 
	}
	AfxGetApp()->BeginWaitCursor(); 

	//被保存的最后有效宽度和高度:w , h.
	LONG w = (LONG)min(nWidth, m_nWidth - x); 
	LONG h = (LONG)min(nHeight, m_nHeight - y); 


	//////////////////////////////////////////////////////
	//第一步:将数据转化为24位DDB数据
	//原DDB数据转换成24位后, 每行的大小和总的数据量
	DWORD dwWidthBytes24 =  ((w * 24 + 15) / 16) * 2; 
	DWORD dwBitsSize24 = dwWidthBytes24 * h; 

	//分配全局内存, 以存放经格式转换后的像素数据

	//指向24位数据的指针
	BYTE* pbyDdbBits24 = new BYTE[dwBitsSize24]; 
	if(pbyDdbBits24 == NULL) return; 
	memset(pbyDdbBits24,  0,  dwBitsSize24); 

	//指向24位像素数据的指针, 这些数据是由16, 24, 32位模式转化而来的.
	GetDdbDataTo24(x,  y,  w,  h,  pbyDdbBits24); 

	
	//////////////////////////////////////////////////////
	//第二步, 获取量化后的数据
	//分配存储8位数据的内存
	DWORD dwWidthBytes8 =  ((w * 8 + 15) / 16) * 2; 
	DWORD dwBitsSize8 = dwWidthBytes8 * h; 

	//指向8位数据的指针
	BYTE* pbyDdbBits8 =  new BYTE[dwBitsSize8]; 
	if (pbyDdbBits8 == NULL)return ; 
	memset(pbyDdbBits8,  0 ,  dwBitsSize8);  

	//////////////////////////////////////////////////////
	//获取量化数据
	CPalette* pPalette = new CPalette(); 
	QuantizeColor(pbyDdbBits24,  w,  h,  pbyDdbBits8,  pPalette); 

	
	//////////////////////////////////////////////////////
	//获取调色数据
	RGBQUAD* pRGBQuad = new RGBQUAD[256]; 
	if(pRGBQuad == NULL)return; 
	//初始化为0
	memset(pRGBQuad, 0, 256 * sizeof(RGBQUAD)); 
	
	PALETTEENTRY* pPE = new PALETTEENTRY[256]; 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -