
CDib::CDib()
{
mBitmapInfo = NULL;
mVideoWindow = NULL;
mWindowDC = NULL;
mNeedStretch = FALSE;
SetRectEmpty(&mTargetRect);
SetRectEmpty(&mSourceRect);
}
CDib::~CDib()
{
if (mBitmapInfo)
{
delete [] (BYTE) mBitmapInfo;
mBitmapInfo = NULL;
}
}
void CDib::SetVideoWindow(HWND inWindow)
{
mVideoWindow = inWindow;
// Get attributes of video window
mWindowDC = ::GetDC(mVideoWindow);
::GetClientRect(mVideoWindow, &mTargetRect);
}
BOOL CDib::SetInputFormat(BYTE inFormat, long inLength)
{
if (mBitmapInfo)
{
delete [] (BYTE) mBitmapInfo;
mBitmapInfo = NULL;
}
mBitmapInfo = (BITMAPINFO ) new BYTE[inLength];
if (NULL == mBitmapInfo)
{
return FALSE;
}
CopyMemory(mBitmapInfo, inFormat, inLength);
// Fill source rectangle
mSourceRectleft = 0;
mSourceRecttop = 0;
mSourceRectright = mBitmapInfo->bmiHeaderbiWidth;
mSourceRectbottom = mBitmapInfo->bmiHeaderbiHeight;
return TRUE;
}
BOOL CDib::Open(void)
{
// Calculate the overall rectangle dimensions
LONG sourceWidth = mSourceRectright - mSourceRectleft;
LONG targetWidth = mTargetRectright - mTargetRectleft;
LONG sourceHeight = mSourceRectbottom - mSourceRecttop;
LONG targetHeight = mTargetRectbottom - mTargetRecttop;
mNeedStretch = TRUE;
if (sourceWidth == targetWidth && sourceHeight == targetHeight)
{
mNeedStretch = FALSE;
}
return TRUE;
}
// Performence is a little bad :(
BOOL CDib::Play(BYTE inData, DWORD inLengt)
{
INT targetWidth = mTargetRectright - mTargetRectleft;
INT targetHeight = mTargetRectbottom - mTargetRecttop;
// To display video smoothly, use a memory DC
HDC memDC = CreateCompatibleDC(mWindowDC);
HBITMAP membmp = CreateCompatibleBitmap(mWindowDC, targetWidth, targetHeight);
HBITMAP oldbmp = (HBITMAP) SelectObject(memDC, membmp);
if (mNeedStretch == FALSE)
{
// Put the image straight into the window
SetDIBitsToDevice(
(HDC) memDC, // Target device HDC
mTargetRectleft, // X sink position
mTargetRecttop, // Y sink position
targetWidth, // Destination width
targetHeight, // Destination height
mSourceRectleft, // X source position
mSourceRecttop, // Y source position
(UINT) 0, // Start scan line
mBitmapInfo->bmiHeaderbiHeight, // Scan lines present
inData, // Image data
(BITMAPINFO ) mBitmapInfo, // DIB header
DIB_RGB_COLORS); // Type of palette
}
else
{
// Stretch the image when copying to the window
StretchDIBits(
(HDC) memDC, // Target device HDC
mTargetRectleft, // X sink position
mTargetRecttop, // Y sink position
targetWidth, // Destination width
targetHeight, // Destination height
mSourceRectleft, // X source position
mSourceRecttop, // Y source position
mSourceRectright - mSourceRectleft, // Source width
mSourceRectbottom - mSourceRecttop, // Source height
inData, // Image data
(BITMAPINFO ) mBitmapInfo, // DIB header
DIB_RGB_COLORS, // Type of palette
SRCCOPY); // Simple image copy
}
BitBlt(mWindowDC, 0, 0, targetWidth, targetHeight, memDC, 0, 0, SRCCOPY);
SelectObject(memDC, oldbmp);
DeleteObject(membmp);
DeleteDC(memDC);
return TRUE;
}
本问介绍了GDI+支持的大多数图像文件格式,以及GDI+提供的处理图像的文件的两个类:Image和Bitmap我们知道,在以往的图像处理中,常常要根据不同图像文件的格式及其数据存储结构在不同格式中进行转换。某个图像文件的显示也是依靠对文件数据结构的剖析,然后读取相关图像数据而实现的。现在,GDI+提供了Image和Bitmap类使我们能轻松容易地处理图像。概述GDI+支持大多数流行的图像文件格式,如BMP、GIF、JPEG、TIFF和PNG等。下面先来介绍这些图像文件,然后再说明Image和Bitmap类支持的特性。1.图像文件格式简介图像文件是描绘一幅图像的计算机磁盘文件,其文件格式不下数十种。这里仅介绍BMP、GIF、JPEG、TIFF和PNG等图像文件格式。BMP文件格式BMP图像文件格式是Microsoft为其Windows环境设置的标准图像格式。一个Windows的BMP位图实际上是一些和显示像素相对应的位阵列,它有两种类型:一种称之为GDI位图,另一种是DIB位图(Device-IndependentBitmap)。GDI位图包含了一种和Windows的GDI模块有关的Windows数据结构,该数据结构是与设备有关的,故此位图又称为DDB位图(Device-DependentBitmap)。当用户的程序取得位图数据信息时,其位图显示方式视显示卡而定。由于GDI位图的这种设备依赖性,当位图通过网络传送到另一台PC,很可能就会出现问题。DIB比GDI位图有很多编程优势,例如它自带颜色信息,从而使调色板管理更加容易。且任何运行Windows的机器都可以处理DIB,并通常以后缀为BMP的文件形式被保存在磁盘中或作为资源存在于程序的EXE或DLL文件中。GIF文件格式图形交换格式(GIF--GraphicsInterchangeFormat)最早由CompuServe公司于1987年6月15日制定的标准,主要用于CompuServe网络图形数据的在线传输、存储。GIF提供了足够的信息并很好地组织了这些信息,使得许多不同的输入输出设备能够方便地交换图像,它支持24位彩色,由一个最多256种颜色的调色板实现,图像的大小最多是64Kx64K个像点。GIF的特点是LZW压缩、多图像和交错屏幕绘图。JPEG文件格式国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)联合成立的"联合照片专家组"JPEG(JointPhotographicExpertsGroup)经过五年艰苦细致工作后,于1991年3月提出了ISOCD10918号建议草案:"多灰度静止图像的数字压缩编码"(通常简称为JPEG标准)。这是一个适用于彩色和单色多灰度或连续色调静止数字图像的压缩标准。它包括无损压缩和基于离散余弦变换和Huffman编码的有损压缩两个部分。前者不会产生失真,但压缩比很小;后一种算法进行图像压缩时,信息虽有损失但压缩比可以很大。例如压缩20~40倍时,人眼基本上看不出失真。JPEG图像文件也是一种像素格式的文件格式,但它比BMP等图像文件要复杂许多。所幸的是,GDI+的Image提供了对JPEG文件格式的支持,使得我们不需要对JPEG格式有太多的了解就能处理该格式的图像。TIFF文件格式TIFF(TaggedImageFormatFile,标志图像文件格式)最早由Aldus公司于1986年推出的,它能对从单色到24位真彩的任何图像都有很好的支持,而且在不同的平台之间的修改和转换是十分容易的。与其它图像文件格式不同的是,TIFF文件中有一个标记信息区用来定义文件存储的图像数据类型、颜色和压缩方法。PNG文件格式PNG(PortableNetworkGraphic,可移植的网络图像)文件格式是由ThomasBoutell、TomLane等人提出并设计的,它是为了适应网络数据传输而设计的一种图像文件格式,用于取代格式较为简单、专利限制严格的GIF图像文件格式。而且,这种图像文件格式在某种程度上甚至还可以取代格式比较复杂的TIFF图像文件格式。它的特点主要有:压缩效率通常比GIF要高、提供Alpha通道控制图像的透明度、支持Gamma校正机制用来调整图像的亮度等。需要说明的是,PNG文件格式支持三种主要的图像类型:真彩色图像、灰度级图像以及颜色索引数据图像。JPEG只支持前两种图像类型,而GIF虽然可以利用灰度调色板补偿图像的灰度级别,但原则上它仅仅支持第三种图像类型。mage和Bitmap类概述GDI+的Image类封装了对BMP、GIF、JPEG、PNG、TIFF、WMF(Windows元文件)和EMF(增强WMF)图像文件的调入、格式转换以及简单处理的功能。而Bitmap是从Image类继承的一个图像类,它封装了Windows位图 *** 作的常用功能。例如,Bitmap::SetPixel和Bitmap::GetPixel分别用来对位图进行读写像素 *** 作,从而可以为图像的柔化和锐化处理提供一种可能。3.DrawImage方法DrawImage是GDI+的Graphics类显示图像的核心方法,它的重载函数有许多个。常用的一般重载函数有:StatusDrawImage(Imageimage,INTx,INTy);StatusDrawImage(Imageimage,constRect&rect);StatusDrawImage(Imageimage,constPointdestPoints,INTcount);StatusDrawImage(Imageimage,INTx,INTy,INTsrcx,INTsrcy,INTsrcwidth,INTsrcheight,UnitsrcUnit);其中,(x,y)用来指定图像image显示的位置,这个位置和image图像的左上角点相对应。rect用来指定被图像填充的矩形区域,destPoints和count分别用来指定一个多边形的顶点和顶点个数。若count为3时,则表示该多边形是一个平行四边形,另一个顶点由系统自动给出。此时,destPoints中的数据依次对应于源图像的左上角、右上角和左下角的顶点坐标。srcx、srcy、srcwidth和srcheight用来指定要显示的源图像的位置和大小,srcUnit用来指定所使用的单位,默认时使用PageUnitPixel,即用像素作为度量单位。调用和显示图像文件在GDI+中调用和显示图像文件是非常容易的,一般先通过Image或Bitmap调入一个图像文件构造一个对象,然后调用Graphics::DrawImage方法在指定位置处显示全部或部分图像。例如下面的代码:voidCEx_GDIPlusView::OnDraw(CDCpDC){CEx_GDIPlusDocpDoc=GetDocument();ASSERT_VALID(pDoc);usingnamespaceGdiplus;Graphicsgraphics(pDC->m_hDC);Imageimage(L"sunflowerjpg");graphicsDrawImage(&image,10,10);Rectrect(130,10,imageGetWidth(),imageGetHeight());graphicsDrawImage(&image,rect);}结果如图717所示,从图中我们可以看出,两次DrawImage的结果是不同的,按理应该相同,这是怎么一回事?原来,DrawImage在不指定显示区域大小时会自动根据设备分辨率进行缩放,从而造成显示结果的不同。当然,也可以使用Bitmap类来调入图像文件来构造一个Bitmap对象,其结果也是一样的。例如,上述代码可改为:Bitmapbmp(L"sunflowerjpg");graphicsDrawImage(&bmp,10,10);Rectrect(130,10,bmpGetWidth(),bmpGetHeight());graphicsDrawImage(&bmp,rect);需要说明的是,Image还提供GetThumbnailImage的方法用来获得一个缩略图的指针,调用DrawImage后可将该缩略图显示,这在图像预览时极其有用。例如下面的代码:Graphicsgraphics(pDC->m_hDC);Imageimage(L"sunflowerjpg");ImagepThumbnail=imageGetThumbnailImage(50,50,NULL,NULL);//显示缩略图graphicsDrawImage(pThumbnail,20,20);//使用后,不要忘记删除该缩略图指针deletepThumbnail;图像旋转和拉伸图像的旋转和拉伸通常是通过在DrawImage中指定destPoints参数来实现,destPoints包含对新的坐标系定义的点的数据。图718说明了坐标系定义的方法。从图中可以看出,destPoints中的第一个点是用来定义坐标原点的,第二点用来定义X轴的方法和图像X方向的大小,第三个是用来定义Y轴的方法和图像Y方向的大小。若destPoints定义的新坐标系中两轴方向不垂直,就能达到图像拉伸的效果。下面的代码就是图像旋转和拉伸的一个示例,其结果如图719所示。Imageimage(L"sunflowerjpg");graphicsDrawImage(&image,10,10);Pointpoints[]={Point(0,0),Point(imageGetWidth(),0),Point(0,imageGetHeight())};Matrixmatrix(1,0,0,1,230,10);//定义一个单位矩阵,坐标原点在(230,10)matrixRotate(30);//顺时针旋转30度matrixScale(063,06);//X和Y方向分别乘以063和06比例因子matrixTransformPoints(points,3);//用该矩阵转换pointsgraphicsDrawImage(&image,points,3);Pointnewpoints[]={Point(450,10),Point(510,60),Point(350,80)};graphicsDrawImage(&image,newpoints,3);当然,对于图像旋转还可直接使用Graphics::RotateTransform来进行,例如下面的代码。但这样设置后,以后所有的绘图结果均会旋转,有时可能感觉不方便。Imageimage(L"sunflowerjpg");graphicsTranslateTransform(230,10);//将原点移动到(230,10)graphicsRotateTransform(30);//顺时针旋转30度graphicsDrawImage(&image,0,0);调整插补算法的质量当图像进行缩放时,需要对图像像素进行插补,不同的插补算法其效果是不一样的。Graphics::SetInterpolationMode可以让我们根据自己的需要使用不同质量效果的插补算法。当然,质量越高,其渲染时间越长。下面的代码就是使用不同质量效果的插补算法模式,其结果如图720所示。Graphicsgraphics(pDC->m_hDC);Imageimage(L"loggif");UINTwidth=imageGetWidth();UINTheight=imageGetHeight();//不进行缩放graphicsDrawImage(&image,10,10);//使用低质量的插补算法graphicsSetInterpolationMode(InterpolationModeNearestNeighbor);graphicsDrawImage(&image,Rect(170,30,(INT)(06width),(INT)(06height)));//使用中等质量的插补算法graphicsSetInterpolationMode(InterpolationModeHighQualityBilinear);graphicsDrawImage(&image,Rect(270,30,(INT)(06width),(INT)(06height)));//使用高质量的插补算法graphicsSetInterpolationMode(InterpolationModeHighQualityBicubic);graphicsDrawImage(&image,Rect(370,30,(INT)(06width),(INT)(06height)));事实上,Image功能还不止这些,例如还有不同格式文件之间的转换等。但这些功能和MFC的新类CImage功能基本一样,但CImage更符合MFC程序员的编程习惯,因此下一节中我们来重点介绍CImage的使用方法和技巧。
本问介绍了GDI+支持的大多数图像文件格式,以及GDI+提供的处理图像的文件的两个类:Image和Bitmap 我们知道,在以往的图像处理中,常常要根据不同图像文件的格式及其数据存储结构在不同格式中进行转换。某个图像文件的显示也是依靠对文件数据结构的剖析,然后读取相关图像数据而实现的。现在,GDI+提供了Image和Bitmap类使我们能轻松容易地处理图像。
概述
GDI+支持大多数流行的图像文件格式,如BMP、GIF、JPEG、TIFF和PNG等。下面先来介绍这些图像文件,然后再说明Image和Bitmap类支持的特性。
1.图像文件格式简介
图像文件是描绘一幅图像的计算机磁盘文件,其文件格式不下数十种。这里仅介绍BMP、GIF、JPEG、TIFF和PNG等图像文件格式。
BMP文件格式
BMP图像文件格式是Microsoft为其Windows环境设置的标准图像格式。一个Windows的BMP位图实际上是一些和显示像素相对应的位阵列,它有两种类型:一种称之为GDI位图,另一种是DIB位图(Device-Independent Bitmap)。GDI位图包含了一种和Windows的GDI模块有关的Windows数据结构,该数据结构是与设备有关的,故此位图又称为DDB位图(Device-Dependent Bitmap)。当用户的程序取得位图数据信息时,其位图显示方式视显示卡而定。由于GDI位图的这种设备依赖性,当位图通过网络传送到另一台PC,很可能就会出现问题。
DIB比GDI位图有很多编程优势,例如它自带颜色信息,从而使调色板管理更加容易。且任何运行Windows的机器都可以处理DIB,并通常以后缀为BMP的文件形式被保存在磁盘中或作为资源存在于程序的EXE或DLL文件中。
GIF文件格式
图形交换格式(GIF--Graphics Interchange Format)最早由CompuServe公司于1987年6月15日制定的标准,主要用于CompuServe网络图形数据的在线传输、存储。GIF提供了足够的信息并很好地组织了这些信息,使得许多不同的输入输出设备能够方便地交换图像,它支持24位彩色,由一个最多256种颜色的调色板实现,图像的大小最多是64K x 64K个像点。GIF的特点是LZW压缩、多图像和交错屏幕绘图。
JPEG文件格式
国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)联合成立的"联合照片专家组"JPEG(Joint Photographic Experts Group)经过五年艰苦细致工作后,于1991年3月提出了ISO CD 10918号建议草案:"多灰度静止图像的数字压缩编码"(通常简称为JPEG标准)。这是一个适用于彩色和单色多灰度或连续色调静止数字图像的压缩标准。它包括无损压缩和基于离散余弦变换和Huffman编码的有损压缩两个部分。前者不会产生失真,但压缩比很小;后一种算法进行图像压缩时,信息虽有损失但压缩比可以很大。例如压缩20~40倍时,人眼基本上看不出失真。
JPEG图像文件也是一种像素格式的文件格式,但它比BMP等图像文件要复杂许多。所幸的是,GDI+的Image提供了对JPEG文件格式的支持,使得我们不需要对JPEG格式有太多的了解就能处理该格式的图像。
TIFF文件格式
TIFF(Tagged Image Format File,标志图像文件格式)最早由Aldus公司于1986年推出的,它能对从单色到24位真彩的任何图像都有很好的支持,而且在不同的平台之间的修改和转换是十分容易的。与其它图像文件格式不同的是,TIFF文件中有一个标记信息区用来定义文件存储的图像数据类型、颜色和压缩方法。
PNG文件格式
PNG(Portable Network Graphic,可移植的网络图像)文件格式是由Thomas Boutell、Tom Lane等人提出并设计的,它是为了适应网络数据传输而设计的一种图像文件格式,用于取代格式较为简单、专利限制严格的GIF图像文件格式。而且,这种图像文件格式在某种程度上甚至还可以取代格式比较复杂的TIFF图像文件格式。它的特点主要有:压缩效率通常比GIF要高、提供Alpha通道控制图像的透明度、支持Gamma校正机制用来调整图像的亮度等。
需要说明的是,PNG文件格式支持三种主要的图像类型:真彩色图像、灰度级图像以及颜色索引数据图像。JPEG只支持前两种图像类型,而GIF虽然可以利用灰度调色板补偿图像的灰度级别,但原则上它仅仅支持第三种图像类型。
mage和Bitmap类概述
GDI+的Image类封装了对BMP、GIF、JPEG、PNG、TIFF、WMF(Windows元文件)和EMF(增强WMF)图像文件的调入、格式转换以及简单处理的功能。而Bitmap是从Image类继承的一个图像类,它封装了Windows位图 *** 作的常用功能。例如,Bitmap::SetPixel和Bitmap::GetPixel分别用来对位图进行读写像素 *** 作,从而可以为图像的柔化和锐化处理提供一种可能。
3.DrawImage方法
DrawImage是GDI+的Graphics类显示图像的核心方法,它的重载函数有许多个。常用的一般重载函数有:
Status DrawImage( Image image, INT x, INT y);
Status DrawImage( Image image, const Rect& rect);
Status DrawImage( Image image, const Point destPoints, INT count);
Status DrawImage( Image image, INT x, INT y, INT srcx, INT srcy,
INT srcwidth, INT srcheight, Unit srcUnit);
其中,(x,y)用来指定图像image显示的位置,这个位置和image图像的左上角点相对应。rect用来指定被图像填充的矩形区域, destPoints和count分别用来指定一个多边形的顶点和顶点个数。若count为3时,则表示该多边形是一个平行四边形,另一个顶点由系统自动给出。此时,destPoints中的数据依次对应于源图像的左上角、右上角和左下角的顶点坐标。srcx、srcy、srcwidth 和srcheight用来指定要显示的源图像的位置和大小,srcUnit用来指定所使用的单位,默认时使用PageUnitPixel,即用像素作为度量单位。
调用和显示图像文件
在GDI+中调用和显示图像文件是非常容易的,一般先通过Image或Bitmap调入一个图像文件构造一个对象,然后调用Graphics::DrawImage方法在指定位置处显示全部或部分图像。例如下面的代码:
void CEx_GDIPlusView::OnDraw(CDC pDC)
{
CEx_GDIPlusDoc pDoc = GetDocument();
ASSERT_VALID(pDoc);
using namespace Gdiplus;
Graphics graphics( pDC->m_hDC );
Image image(L"sunflowerjpg");
graphicsDrawImage(&image, 10,10);
Rect rect(130, 10, imageGetWidth(), imageGetHeight());
graphicsDrawImage(&image, rect);
}
结果如图717所示,从图中我们可以看出,两次DrawImage的结果是不同的,按理应该相同,这是怎么一回事?原来,DrawImage在不指定显示区域大小时会自动根据设备分辨率进行缩放,从而造成显示结果的不同。
当然,也可以使用Bitmap类来调入图像文件来构造一个Bitmap对象,其结果也是一样的。例如,上述代码可改为:
Bitmap bmp(L"sunflowerjpg");
graphicsDrawImage(&bmp, 10,10);
Rect rect(130, 10, bmpGetWidth(), bmpGetHeight());
graphicsDrawImage(&bmp, rect);
需要说明的是,Image还提供GetThumbnailImage的方法用来获得一个缩略图的指针,调用DrawImage后可将该缩略图显示,这在图像预览时极其有用。例如下面的代码:
Graphics graphics( pDC->m_hDC );
Image image(L"sunflowerjpg");
Image pThumbnail = imageGetThumbnailImage(50, 50, NULL, NULL);
// 显示缩略图
graphicsDrawImage(pThumbnail, 20, 20);
// 使用后,不要忘记删除该缩略图指针
delete pThumbnail;
图像旋转和拉伸
图像的旋转和拉伸通常是通过在DrawImage中指定destPoints参数来实现,destPoints包含对新的坐标系定义的点的数据。图718说明了坐标系定义的方法。
从图中可以看出,destPoints中的第一个点是用来定义坐标原点的,第二点用来定义X轴的方法和图像X方向的大小,第三个是用来定义Y轴的方法和图像Y方向的大小。若destPoints定义的新坐标系中两轴方向不垂直,就能达到图像拉伸的效果。
下面的代码就是图像旋转和拉伸的一个示例,其结果如图719所示。
Image image(L"sunflowerjpg");
graphicsDrawImage(&image, 10,10);
Point points[] = { Point(0, 0), Point(imageGetWidth(), 0),
Point(0, imageGetHeight())};
Matrix matrix(1,0,0,1,230,10); // 定义一个单位矩阵,坐标原点在(230,10)
matrixRotate(30); // 顺时针旋转30度
matrixScale(063,06); // X 和 Y 方向分别乘以063和06比例因子
matrixTransformPoints(points, 3); // 用该矩阵转换points
graphicsDrawImage(&image, points, 3);
Point newpoints[] = {Point(450, 10), Point(510, 60), Point(350, 80)};
graphicsDrawImage(&image, newpoints, 3);
当然,对于图像旋转还可直接使用Graphics::RotateTransform来进行,例如下面的代码。但这样设置后,以后所有的绘图结果均会旋转,有时可能感觉不方便。
Image image(L"sunflowerjpg");
graphicsTranslateTransform(230,10); // 将原点移动到(230,10)
graphicsRotateTransform(30); // 顺时针旋转30度
graphicsDrawImage(&image, 0,0);
调整插补算法的质量
当图像进行缩放时,需要对图像像素进行插补,不同的插补算法其效果是不一样的。Graphics:: SetInterpolationMode可以让我们根据自己的需要使用不同质量效果的插补算法。当然,质量越高,其渲染时间越长。下面的代码就是使用不同质量效果的插补算法模式,其结果如图720所示。 Graphics graphics( pDC->m_hDC );
Image image(L"loggif");
UINT width = imageGetWidth();
UINT height = imageGetHeight();
// 不进行缩放
graphicsDrawImage( &image,10,10);
// 使用低质量的插补算法
graphicsSetInterpolationMode(InterpolationModeNearestNeighbor);
graphicsDrawImage( &image,
Rect(170, 30, (INT)(06width), (INT)(06height)));
// 使用中等质量的插补算法
graphicsSetInterpolationMode(InterpolationModeHighQualityBilinear);
graphicsDrawImage( &image,
Rect(270, 30, (INT)(06width), (INT)(06height)));
// 使用高质量的插补算法
graphicsSetInterpolationMode(InterpolationModeHighQualityBicubic);
graphicsDrawImage( &image,
Rect(370, 30, (INT)(06width), (INT)(06height)));
事实上,Image功能还不止这些,例如还有不同格式文件之间的转换等。但这些功能和MFC的新类CImage功能基本一样,但CImage更符合MFC程序员的编程习惯,因此下一节中我们来重点介绍CImage的使用方法和技巧。
摘要:介绍嵌入式数字卫星接收机的字符显示原理。提出一种提取Windows矢量字库信息以及将其转换为点阵字库格式的方法。给出主要模块的流程图和VC程序实现。在DVB-S接收机顶盒系统中的应用验证了该方法的有效性。
关键词:矢量字库 机顶盒 字符转换
引言
在当前嵌入式系统设计中,广泛涉及到字符和图标的显示。过去的方法通常是使用硬件自带字库或者转换中文 *** 作系统(例如UCDOS)的点阵字库,但随着嵌入式开发技术的发展,人们对用户界的要求也越来截止高,大小固定、字体有限、使用不便的点阵字库已经难再满足要求。于是众多开发人员将目光投向了Windows *** 作系统丰富的字库和图标资源,以Widnows 2000系统为例,其OpenType矢量字库有基于Unicode内码的四万多个字符,特殊符号以及近百种字体可以选用。但是Windows系统结构复杂,难以直接提取矢量字库,并且矢量字库解析算法涉及到Micorosoft和Adobe公司OpenType字体专利,这些问题为Windows字库的使用带来了很大的困难。
在笔者参与的DVB-S数字卫星接收机顶盒用户界面的设计工作中,也遇到了同样的问题。按照设计要求,需要用到一套完备的,支持包括俄、德、拉丁、阿拉伯等多种语言和特殊符号的小型字库,但是难以找到合适的现成字库,在参考了一些商业字模提取软件的功能后,提出了一种提取Windows矢量字符以及图标资源的方法,并用Visual C++60程序实现。实际应用表明,用本方法生成的字库字形美观,字体多样,完全可以替代商业字库,同时还具有使用方便,便于扩展等优点。
1 机顶盒图文显示原理
机顶盒中文字和位图的显示是通过OSD(图文屏显技术)模块完成的。传统的屏显示OSD主要应用在VCR、LD和电视机上,采用专用的芯片修改屏幕上指定部位的信号的角度和亮度,实现模拟视频的同步改变,从而达到显示的目的。含有OSD的视频输出信号在屏幕上从底向上以6个层次显示:边界颜色、静止视频图像、活动视频图像、OSD背窗口、OSD位图区域和OSD硬件游标。本文介绍的机顶盒系统是基于ST公司的方案,接收机软件是ST公司为其数字卫星接收机硬件评估板配套提供的。其主芯片采用Sti5518微控制器,内部集成OSD处理单元。OSD功能模块框图如图1所示。
OSD功能模块位于NTSC/PAL/SECAM编码之前,包括一张颜色查找表(LUTs)、Alpha混合滤波器和控制逻辑单元,所有子功能先于将信息从重建缓冲区传到SDRAM或与其它静态图像混合前执行。
当要输出图文信息时,将字符图标的位图信息送至OSD位图区域的相应位置。OSD位图区域由其头部定义,每个OSD头主要包括OSD显示短形区域的起始位置、大小及两个分别指向顶场和底场图像数据的指针(这是针对隔行扫描显示方式;对于逐行扫描,这两个指针向同一块内存区域),还有一个指向下一个OSD位图数据头的指针。由于采用了这种基于指针的OSD数据管理结构,理论上OSD位图数据块的数目不受限制,实际上它要受到内存大小的限制。头部不仅定义了位图区域的尺寸、位置以及及颜色信息,而且提供了颜色表更新等功能。字符的颜色设置使用OSD处理单元(LUT)的颜色查找表,也称做调色板。2位的LUT意味着有4种颜色可以选择,并且位图中的每个像素仅占有存储单元的2位。如果是透明文字,还要把第一个像素的调色板颜色值定为透明色掩码值,这个过程由Alpha混合处理完成。如果输出像素不在OSD区域,停止处理视频接口处理器数据;如果输出像素在OSD区域,OSD数据或OSD和视频接口处理数据的混合数据经Alpha混合滤波处理后以16位YC(Cb,Cr)格式传输。对于调色板颜色值是透明的情况,则直接传送视频数据而略过OSD位图数据。
OSD的软件部分可以分为两部分:硬件抽象层和图形函数接口。OSD模块软件部分为整个系统软件部分提供一系列的图形函数接口,是实现图文显示的基础,也是给用户提供一个方便直观的图形文字交互方式的保障。本文中硬件抽象层为ST公司提供的STAPI函数库,图形函数接口在中间件的基础上自行开发。
由此可见,在机顶盒系统中字符输出有三个主要步骤:①系统专用字库的建立;②字符数据的查找;③调用OSD模块功能将字符在屏幕上输出。下面介绍如何通过转换Windows矢量字符,建立一套功能完善,使用方便的字库系统。
2 提取矢量字符
Winodws矢量字库存储汉字的矢量图形。因为存储的是笔样条,对于字符做旋转、缩放、甚至三维拉伸都不会产生失真,但在字符显示的时候需要计算样条曲线而增加了计算量。由于嵌入式系统只是针对专一控制应用的系统,处理器的性能和资源还不如PC机,一般使用的仍然是点阵字库。本文介绍的DVB-S机顶盒系统同样没有直接使用矢量字库;而是通过提取Windows中矢量字库的方法将矢量字符转换成相应的点阵信息。在本开发方案中,字库文件中所有汉字的字模信息和图标信息被存储到两个大的数组中,并作为一个头文件包含在汉字显示模块中。利用计算出的偏移值得到字模数组中的下标,从而得到汉字存放在数组中的字模点阵信息。使用程序存储器空间做字库,这在汉字用量不大的情况下是一种较完美的解决方案。本系统中负责屏显功能的API函烽是STOSD函数库,里面已把位图的宽度定义为32像素的整数倍。这是由于系统的内存 *** 作函数只能对16字节整数倍的块进行拷贝 *** 作,否则内存只能一个字节一个字节地填充,速度非常慢。例如在16色的调色板情况下,无论是24×24点阵还是32×32点阵,基于字符单元宽度统一为32像素。一个宽为32像素,高为28像素的基本字符点阵信息需要一个大小为28字节的整型数组为记录。以开发方案为例,大小为500字符的24×24点阵小型字库将需要24 000字节的ROM空间。
转换的关键是要获得矢量字库的点阵信息。程序中,回避了较困难的直接解板矢量字库问题,巧妙地从PC显示缓冲区中获得位映像数据,再将其转换成OSD模块函数支持的点阵格式。位图法转换矢量字符的算法如下。
①把汉字以位图的形式显示在指定的32×28的点阵区域内,然后按行提取像素点,每1行以8个像素点为1个字节(1行4个字节),以二进制补码的形式分别存放在4个字节里。最低字节存放每一点阵行的前8位。每一行结束后将其转化为十六进制点阵码并保存于一个整型数组中。
②整个字符转换结束后放在字库信息文件里,生成一个字符区域地址映射表,为后面的字符分组查找提供方便。同时生成字符宽度、高度、字体、风格以及代码页等相关信息。
西文“特殊”字符(拉丁字符集里的第128~255字符,码值大于0x80)的转换是提取矢量字符过程中需要注意的问题。通过VC 60开发环境可以把执行文件编译成Unicode和ASCII两片版本。对于Unicode内码版本的应用程序,Windows 2000对其字符的显示有着很好的支持,但对于ASCII版本的应用程序则存在一定问题。当在应征程序中输入字符时,因编辑框只支持单字节,系统会将双字节的Unicode输入字符重新解释,造成的后果是程序无法正确接收这些字符,输出的特殊字符也一律被显示为“?”。本文的矢量字库提取程序为了和Win9x *** 作系统兼容而被设计为ASCII版本。为了解决上述问题,程序沿用了代码页的方法。代码页是一个内部表, *** 作系统将字符、数字和标点等符号映射为字符编号,不同的代码而支持不同国家所使用的字符集。代码页通过编号引用,例如,代码页932代表日 本字符集,950代表繁体中文字符集。由代码页确定字符集,首先把需要转换的特殊矢量字符编辑后以RTF富文本格式的文本保存,矢量字符提取程序打开RTF文件并插入文本到视图中,读取每个特殊字符的值并转换成十六进制。然后读取RTF文件内的代码页编号和字体,尺寸和风格等标签。根据代码页确定对应的字符集,根据标签设置显示字符属性。最后按一般字符的输出方法将特殊字符显示在视图中。
3 程序实现
3.1 功能设计和界面设计
主程序为MFC生成的SDI单文档程序。视图类由CscrollView派生,显示的字符和位图可以自由地放大和缩小,当字符超过窗口大小时视图自动滚动,以满足提取不同大小库点阵的需要,用一个RichEditBox控件来接收输入字符。添加静态控件,显示字符点阵的宽、高等信息。在菜单栏分别添加插入位图、插入图标、插入特殊字符、字体设置、字体放大和字体缩小等菜单项。
程序的界面如图2所示。图中程序正在提取阿拉伯矢量字符集,使用该程序时直接在该工具的图形 *** 作界面下输入需要提取的字符或者插入位图和图标。待调整好全部所需的字符图标后点击保存,程序自动转换矢量字符和图形并生成存入文件。实现的功能有:①能在视图显示RichEditBox控件内输入英文、汉字等矢量字符,并通过图形设备上下文CDC读取视图的点阵信息;②能读取所有插入RTF文本内的特殊字体点阵信息;③能读取插入的位图和图标点阵信息;④能将点阵信息保存在fonth文件中,并添加字库索引表和字符宽、高、字体等信息。
3.2 主要类和模块
CfontView为CscrollView派生类,负责字符和图标的缩放显示,CfontModule类封装了字符串 *** 作函数,CtextSetDlg类负责字符属性的设定。程序中点阵信息的数据、位图和图标的数据和字符串数据分别封装在类CdotMatrix、CimageElemnt和CwordElement中。在主要的模块函数里,Create_Text_Dot_Matrix和Create_Bmp_Dot_Matrix函数是本程序的核心函数。功能是在内存中形式位映射数据,完成矢量汉字或矢量图形向点阵数据的转换。设向量图型尺寸宽width像素,高height像素,程序流程如下:
①计算该位图对应的缓冲区尺寸。每行长度为:BytePerLine=(width+1)/8,缓冲区大小为Buffersize=BytePerLineheight。
②申请内存缓冲区。如果内存不足以容纳整 个图形,则可以分段处理。
BufferPtr=(unsigned char)malloc(BufferSize)。
③计算坐标点在所申请内存缓冲区的偏移量和屏蔽位。设原点(0,0)在内存中的偏移量为0,则图中任意一点P(x,y)相对于原点(0,0)的偏移量为
offset=yBytePerLine+x/8,该点对应的字节内屏蔽位为mask=0x80>>(X%8)。
④读取点P(x,y)在内存中对应的颜色值value,读取所在的字节。
byte=(unsigned char)(Bufferptr+offset),取该点对应的位,
value=byte & mask最后得到点阵信息,输出到屏幕或磁盘文件。
3.3 主程序流程
主程序流程图如图3所示。首先进入响应用户消息分支,当从编辑框输入标准汉字或ASCII字符,程序检查字符同码判断字符有效性,若满足条件则跳至显示部分;当用户从外部磁盘插入图标或位图,程序直接读入文件数据;如果插入的是特殊字符则进入RTF格式文本解析部分,得到特殊字符的代码页、字体、大小等信息。然后将字符图标信息在视图中显示,此时可通过图形界面调整字符外观。通过获得内存中图像信息形成位映射数据,计算点阵数据。最后将字库点阵和索引表、字体、大小等附加信息按头文件格式保存到字库文件中。程序源代码见网>
以上就是关于VC++怎样生成灰度图全部的内容,包括:VC++怎样生成灰度图、用VC++代码怎样实现图像网络传送(注意:不是图片而是图像)、如何给JPEG图像加水印,要VC代码(急)等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)