C++图像处理 -- 图像颜色混合(中)

阅读提示

    《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。

    《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。

    尽可能保持二者内容一致,可相互对照。

    本文代码必须包括《C++图像处理 -- 数据类型及公用函数》文章中的BmpData.h头文件。

 

     文章《C++图像处理 -- 图像颜色混合(上)》发表后,获得了很多朋友的好评,特别是喜爱研究图形编程的朋友。还有几位好友给我发了邮件,有位朋友在来信中说,虽然Photoshop图层混合模式的原理及公式网上都能搜索到,但大都是一些概念性的东西,很少有具体实现代码,特别是准确性比较高的实现代码。还有朋友问不用GDI+,其它图形数据是否也能用文章中代码进行颜色混合(这位可能是初学者)。

    本文在《C++图像处理 -- 图像颜色混合(上)》基础上作一些优化,如ColorMix函数,主要是为了较详细地阐明颜色混合模式的原理,本文将重新改写该函数。另外,对染色函数也进行了速度上的优化。为了避免去《C++图像处理 -- 图像颜色混合(上)》查找一些数据类型和函数,下面将所有未修改的代码也复制在这里,当然,要搞清楚颜色混合模式的原理或了解运算公式,还得去看《C++图像处理 -- 图像颜色混合(上)》:

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

typedef FLOAT		BWParams, *PBWParams;

// 黑白调整缺省参数:红,黄,绿,洋红,蓝,青
CONST INT _BWDefault[] = {410, 614, 410, 819, 205, 614};

enum
{
	BWIndexBlue		= 0x40000,
	BWIndexGreen	= 0x20000,
	BWIndexRed		= 0x00000
};

enum
{
	IndexBlue	= 0x00000,
	IndexGreen	= 0x10000,
	IndexRed	= 0x20000
};

typedef union 				// 颜色分量交换结构
{
	INT tmp;				// 交换时用的临时变量
	struct
	{
		SHORT value;		// 颜色分量值
		SHORT index;		// 颜色分量索引
	};
}RGBIndex;
//---------------------------------------------------------------------------

// 交换像素分量
FORCEINLINE
VOID SwapRgb(RGBIndex &a, RGBIndex &b)
{
	a.tmp ^= b.tmp;
	b.tmp ^= a.tmp;
	a.tmp ^= b.tmp;
}
//---------------------------------------------------------------------------

// 获取黑白灰度
FORCEINLINE
INT	GetBWGray(CONST PARGBQuad pixel, CONST PINT bwParams)
{
	RGBIndex max, mid, min;
	min.tmp = pixel->Blue | BWIndexBlue;
	mid.tmp = pixel->Green | BWIndexGreen;
	max.tmp = pixel->Red | BWIndexRed;

	if (max.value < mid.value)
		SwapRgb(max, mid);
	if (max.value < min.value)
		SwapRgb(max, min);
	if (min.value > mid.value)
		SwapRgb(min, mid);

	return (((max.value - mid.value) * bwParams[max.index] +
		(mid.value - min.value) * bwParams[max.index + mid.index - 1] +
		512) >> 10) + min.value;
}
//---------------------------------------------------------------------------

VOID ColorMix(PARGBQuad pd, CONST PARGBQuad ps, INT gray)
{
	// 灰度计算常数:蓝,绿、红
	CONST INT ys[3] = {113, 604, 307};

	RGBIndex max, mid, min;
	min.tmp = ps->Blue | IndexBlue;
	mid.tmp = ps->Green | IndexGreen;
	max.tmp = ps->Red | IndexRed;

	if (max.value < mid.value)
		SwapRgb(max, mid);
	if (max.value < min.value)
		SwapRgb(max, min);
	if (min.value > mid.value)
		SwapRgb(min, mid);

	INT max_min = max.value - min.value;
	// 饱和度为0,返回灰度
	if (max_min == 0)
	{
		pd->Blue = pd->Green = pd->Red = gray;
		return;
	}
	INT mid_min = mid.value - min.value;

	INT newMax, newMid, newMin;
	gray <<= 10;
	newMax = (gray + (max_min - mid_min) * ys[mid.index] + max_min * ys[min.index] + 512) >> 10;
	newMin = newMax - max_min;
	if (newMax > 255)
	{
		INT hueCoef = (mid_min << 10) / max_min;
		INT v0 = (ys[mid.index] * hueCoef) >> 10;
		INT v1 = ys[min.index] + ys[mid.index] - v0;
		newMin = (gray - (ys[max.index] + v0) * 255 + (v1 >> 1)) / v1;
		newMid = newMin + (((255 ^ newMin) * hueCoef + 512) >> 10);
		newMax = 255;

	}
	else if (newMin < 0)
	{
		INT hueCoef = (mid_min << 10) / max_min;
		INT tmp = ys[max.index] + ((ys[mid.index] * hueCoef + 512) >> 10);
		newMax = (gray + (tmp >> 1)) / tmp;
		newMid = (newMax * hueCoef + 512) >> 10;
		newMin = 1;
	}
	else
		newMid = newMin + mid_min;

	((LPBYTE)pd)[max.index] = newMax;
	((LPBYTE)pd)[mid.index] = newMid;
	((LPBYTE)pd)[min.index] = newMin;
}
//---------------------------------------------------------------------------

// 图像黑白调整。
// 调整参数bwParams为元素数等于6的数组指针,分别为红,黄,绿,青,蓝,洋红
VOID ImageBlackWhite(BitmapData *data, CONST PBWParams bwParams = NULL)
{
	// 拷贝像素灰度参数,并交换青色和洋红色
	INT params[6], *pparams;
	if (bwParams)
	{
		for (INT i = 0; i < 6; i ++)
			params[i] = (INT)(bwParams[i] * 1024 + 0.5);
		params[3] ^= params[5];
		params[5] ^= params[3];
		params[3] ^= params[5];
		pparams = params;
	}
	else
		pparams = (INT*)_BWDefault;

	PARGBQuad p = (PARGBQuad)data->Scan0;
	INT dataOffset = (data->Stride >> 2) - (INT)data->Width;

	for (UINT y = 0; y < data->Height; y ++, p += dataOffset)
	{
		for (UINT x = 0; x < data->Width; x ++, p ++)
		{
			INT gray = GetBWGray(p, pparams);
			p->Blue = p->Green = p->Red =
				(gray & ~0xff) == 0? gray : gray > 255? 255 : 0;
		}
	}
}
//---------------------------------------------------------------------------

VOID ImageTint(BitmapData *grayData, ARGB color)
{
	ARGBQuad colorTable[256];
	PARGBQuad p = colorTable;

	for (INT i = 0; i < 256; i ++, p ++)
	{
		ColorMix(p, (PARGBQuad)&color, i);
		p->Alpha = 255;
	}

	p = (PARGBQuad)grayData->Scan0;
	INT dataOffset = (grayData->Stride >> 2) - (INT)grayData->Width;

	for (UINT y = 0; y < grayData->Height; y ++, p += dataOffset)
	{
		for (UINT x = 0; x < grayData->Width; x ++, p ++)
		{
			p->Color = colorTable[p->Blue].Color;
		}
	}
}
//---------------------------------------------------------------------------

// 图像颜色模式混合
VOID ImageColorMixer(BitmapData *dest, CONST BitmapData *source)
{
	PARGBQuad pd, ps;
	UINT width, height;
	INT dstOffset, srcOffset;
	GetDataCopyParams(dest, source, width, height, pd, ps, dstOffset, srcOffset);

	for (UINT y = 0; y < height; y ++, pd += dstOffset, ps += srcOffset)
	{
		for (UINT x = 0; x < width; x ++, pd ++, ps ++)
		{
			ColorMix(pd, ps, GetBWGray(pd, (PINT)_BWDefault));
		}
	}
}
//---------------------------------------------------------------------------

    改写后的ColorMix函数全部采用定点数运算。而染色函数ImageTint采用了查表法,一次性计算一个256色灰度的染色表,只需以灰度图象各个像素的灰度值为下标进行直接赋值即可,比起未修改前的逐像素调用ColorMix函数计算染色值,无疑是大大提高了运行效率。

    本文下篇将着重进行颜色混合模式功能的完善。

 

    因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:[email protected]

    这里可访问《C++图像处理 -- 文章索引

你可能感兴趣的:(C++,C++,c,c,GDI+,GDI+,Mixer,颜色混合)