C++图像处理 -- PCX格式图像(下)

阅读提示

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

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

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

 

    《C++图像处理 -- PCX格式图像(上)》将PCX格式图像转换为GDI+位图,本文则介绍将GDI+位图转换为PCX格式图像。

    下面是GDI+位图转换为PCX格式图像代码:

INT PackPcxLine(LPBYTE dest, LPBYTE source, INT bytesPreLine, INT planes)
{
	LPBYTE pd = dest;
	INT delta = planes --;
	LPBYTE ps = source + planes;
	INT bytes = bytesPreLine;
	while(planes >= 0)
	{
		INT count = 0;
		BYTE c = *ps;
		do
		{
			count ++;
			if (-- bytes == 0)
			{
				if (-- planes < 0) break;
				bytes = bytesPreLine;
				ps = source + planes;
			}
			else ps += delta;
		} while(c == *ps && count < 0x3f);
		if (c >= 0xc0 || count > 1)
			*pd ++ = count | 0xc0;
		*pd ++ = c;
	}
	return pd - dest;
}
//---------------------------------------------------------------------------

typedef union
{
	WORD value;
	struct
	{
		BYTE low;
		BYTE high;
    };
}testMask;

INT PackPcx4Line(LPBYTE dest, LPBYTE source, INT bytesPreLine, INT width)
{
	INT bytes = bytesPreLine << 2;
	LPBYTE buf = dest + bytes;
	testMask mask;
	mask.value = 0x1001;
	width = (width + 1) >> 1;
	while(mask.high)
	{
		BYTE c = 0;
		BYTE bit = 0x80;
		LPBYTE pb = buf;
		for (INT i = 0; i < width; i ++)
		{
			if (source[i] & mask.high) c |= bit;
			bit >>= 1;
			if (source[i] & mask.low) c |= bit;
			bit >>= 1;
			if (bit == 0)
			{
				*pb ++ = c;
				c = 0;
				bit = 0x80;
			}
		}
		buf += bytesPreLine;
		mask.value <<= 1;
	}
	return PackPcxLine(dest, dest + bytes, bytes, 1);
}
//---------------------------------------------------------------------------

VOID ARGBQuadToRGBTriple(PRGBTriple dest, PRGBQuad source, INT count)
{
	for (INT i = 0; i < count; i++)
	{
		dest[i].rgbtBlue = source[i].rgbRed;
		dest[i].rgbtGreen = source[i].rgbGreen;
		dest[i].rgbtRed = source[i].rgbBlue;
    }
}
//---------------------------------------------------------------------------

BOOL SavePcxImageToStream(IStream *stream, Bitmap *bmp)
{
	PixelFormat format = bmp->GetPixelFormat();
	if (format == PixelFormatUndefined)
		return FALSE;
	PcxFileHeader header;
	ColorPalette *pal = NULL;
	BYTE palette[256 * 3 + 1];
	ZeroMemory(&header, sizeof(PcxFileHeader));
	header.bitsPrePixel = GetPixelFormatSize(format);
	header.planes = 1;
	if (header.bitsPrePixel > 8)
	{
		format = PixelFormat24bppRGB;
		header.planes = 3;
		header.bitsPrePixel = 8;
	}
	else //if (header.bitsPrePixel > 1)
	{
		pal = (ColorPalette*)new BYTE[256 * sizeof(ARGB) + sizeof(ColorPalette)];
		bmp->GetPalette(pal, bmp->GetPaletteSize());
		PRGBTriple ppal = (PRGBTriple)&palette[1];
		// 如果是16色位图,调色板保存到文件头
		if (format == PixelFormat4bppIndexed)
		{
			header.planes = header.bitsPrePixel;
			header.bitsPrePixel = 1;
			ppal = (PRGBTriple)header.palette;
		}
		ARGBQuadToRGBTriple(ppal, (PRGBQuad)pal->Entries, pal->Count);
		delete[] pal;
	}
	Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
	header.flag = 0x0A;
	header.version = 5;
	header.encodeing = 1;
	header.xMax = r.Width - 1;
	header.yMax = r.Height - 1;
	header.hRes = 96;
	header.vRes = 96;
	header.paletteType = 1;
	header.bytesPreLine = (r.Width * header.bitsPrePixel + 7) >> 3;
	if (header.bytesPreLine & 1)
		header.bytesPreLine ++;
	// 保存PCX文件头
	if (stream->Write(&header, sizeof(PcxFileHeader), NULL) != S_OK)
		return FALSE;
	// 获取GDI+位图数据到位图数据结构
	BitmapData data;
	data.Stride = ((r.Width * GetPixelFormatSize(format) + 31) & -32) >> 3;
	INT size = r.Height * data.Stride;
	// size为位图数据字节数,header.bytesPreLine*header.planes*2为编码缓冲区字节数
	data.Scan0 = (LPVOID)new BYTE[size + header.bytesPreLine * header.planes * 2];
	bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf, format, &data);
	bmp->UnlockBits(&data);
	// 如果单色位图调色板首项不为0,位图数据反向
	if (format == PixelFormat1bppIndexed && (*(ARGB*)&palette[1] & 0xffffff))
	{
		INT count = data.Height * (data.Stride >> 2);
		LPDWORD pd = (LPDWORD)data.Scan0;
		for (INT i = 0; i < count; pd[i] ^= (DWORD)(-1), i ++);
	}
	LPBYTE p = (LPBYTE)data.Scan0;
	LPBYTE buffer = p + size;
	INT bytes;
	// 逐行进行RLE编码并保存到流
	for (UINT y = 0; y < data.Height; y ++, p += data.Stride)
	{
		if (format == PixelFormat4bppIndexed)
			bytes = PackPcx4Line(buffer, p, header.bytesPreLine, data.Width);
		else
			bytes = PackPcxLine(buffer, p, header.bytesPreLine, header.planes);
		stream->Write(buffer, bytes, NULL);
	}
	delete[] data.Scan0;
	// 如果是256色位图,调色板保存到流的尾部
	if (format == PixelFormat8bppIndexed)
	{
		palette[0] = 0x0c;
		stream->Write(palette, 256 * 3 + 1, NULL);
	}
	return TRUE;
}
//---------------------------------------------------------------------------

BOOL SavePcxImageToFile(LPTSTR fileName, Bitmap *bmp)
{
	IStream *stream = new FileStream(fileName, FALSE);
	stream->AddRef();
	BOOL result = SavePcxImageToStream(stream, bmp);
	stream->Release();
	return result;
}
//---------------------------------------------------------------------------

    代码中SavePcxImageToStream函数已经将大致的转换流程作了注释,本文不再罗嗦,而SavePcxImageToFile函数仍然是利用我写的简易文件流将转换后的PCX格式图像保存到文件,FileStream类在本文上篇《C++图像处理 -- PCX格式图像(上)》中。

    下面是个GDI+位图转换为PCX格式图像例子(BCB2010):

void __fastcall TForm1::Button3Click(TObject *Sender)
{
	Gdiplus::Bitmap *bmp = new Gdiplus::Bitmap(L"d:\\1-4.bmp");
	if (bmp->GetLastStatus() != Ok)
		throw new Exception("Load Image fail");
	Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
	g->DrawImage(bmp, 0, 0);
	delete g;
	SavePcxImageToFile("d:\\1-4.pcx", bmp);
	delete bmp;
}

    同样,关于PCX文件格式,请网上搜索有关文档。

 

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

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

你可能感兴趣的:(C++图像处理 -- PCX格式图像(下))