vc下打印透明背景图片

一、前言

  刚接到个任务,要把带有透明背景的章子图片打印出来,开始觉得不是很简单吗,直接用vc自动生成的打印功能不就ok了。不过问题却不是想像的那么简单!

二、窗口中显示透明图片

  在窗口中显示图片,可以用强大的CImage类,这个类能加载很多常见格式的图像文件,当然对于我要加载的png格式的透明图片也是可以的。具体的代码如下:

1 CImage m_image;
2  
3 m_image.Load(_T("picture.png"));
4 if (m_image.IsNull())
5 {
6     MessageBox(_T("图片没加载成功"));
7     return;
8 }

  加载png图片到CImage对象后,CImage提供了一个函数Draw,可以直接将图片画到窗口上下文中,在CDrawView类的OnDraw加入下面的代码:

  m_Image.Draw(pDC->m_hDC,0,0);

  就这几行代码就可以了,直接点运行,图片确实画到了窗口上,不过却不是透明显示的。透明图片中每个像素都有一个ALPHA(0-255)值来表示透明的程度,如果某个像素不是透明的,那么它的ALPHA为0。一般在已有像素上画带有ALPHA值的像素,可以用下面的方法实现:假设源像素的RGB分别为srcR,srcG,srcB,要画的带有ALPHA值的像素的RGB和ALPHA为desR,desG,desB和desA,新像素的RGB为newR,newG,newB。具体计算方法为:

  newR = srcR * (1-desA) / 255 + desR * desA / 255

  newG = srcR * (1-desA) / 255 + desG * desA / 255

  newB = srcB * (1-desA) / 255 + desB * desA / 255

  CImage类中的Draw函数的实现确实根据下面实现的:

     newR = srcR * (1-desA) / 255 + desR 

  newG = srcR * (1-desA) / 255 + desG 

  newB = srcB * (1-desA) / 255 + desB 

  对于超过255的,都按255处理,显然,如果desR,desG,desB比较大的话,最终得到的newR,newG,newB都将会是255,导致图片的背景都是白色。所以在调用Draw函数之前必须先对desR,desG,desB进行下面的处理:

  desR = desR * desA / 255

  desG = desG * desA / 255

  desG = desG * desA / 255

  具体的实现代码如下:

 1 for(int i = 0; i < m_image.GetWidth(); i++)  
 2 {  
 3     for(int j = 0; j < m_image.GetHeight(); j++)  
 4     {  
 5         unsigned char* pucColor = reinterpret_cast<unsigned char *>(m_image.GetPixelAddress(i , j));  
 6         pucColor[0] = pucColor[0] * pucColor[3] / 255;  //pucColor[3]为ALPHA值
 7         pucColor[1] = pucColor[1] * pucColor[3] / 255;  
 8         pucColor[2] = pucColor[2] * pucColor[3] / 255;  
 9     }  
10 } 

三、放大打印预览中图片的大小

  直接点打印预览的时候,发现图片显示的很小。这是由于显示器设备每英寸的像素是96,而打印机每英寸的像素是600,也就是说打印机预览显示的图像的大小只有显示器显示图像的1/6。必须要放大打印机显示视口的比例,使得其显示的图像和显示器中显示的图像大小一致。办法是在CDrawView类中重载OnPrepareDC函数,具体代码如下:

 1 void CDrawView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
 2 {
 3     // TODO: 在此添加专用代码和/或调用基类
 4     CView::OnPrepareDC(pDC, pInfo);
 5     pDC->SetMapMode(MM_ANISOTROPIC);//转换坐标映射方式
 6     CSize size=CSize(1024,768);
 7     pDC->SetWindowExt(size);
 8     //得到实际设备每逻辑英寸的象素数量
 9     int xLogPixPerInch=pDC->GetDeviceCaps(LOGPIXELSX);
10     int yLogPixPerInch=pDC->GetDeviceCaps(LOGPIXELSY);
11     long xExt=(long)size.cx*xLogPixPerInch/96;
12     long yExt=(long)size.cy*yLogPixPerInch/96;
13     //确定视口大小
14     pDC->SetViewportExt((int)xExt,(int)yExt);
15 }
16     

  这样使用打印预览的时候,图像的大小就恢复正常了。本以为到这里就大功告成了,没想到点打印的时候,居然打出的是白纸(汗!!)。

四、打印出透明图片

  打印预览能显示出来,却打印不出来,这让我百思不得其解啊!后来想想也许是Draw函数与打印机设备不兼容,然后换做CDC的SetPixel来画图像,结果还是打印不出来,就这样白白浪费了一天的时间啊!过了一天突然想到是不是打印机的问题或者是打印程序的问题,于是在窗口上打印几个字来显示看看,发现可以打印出来,说明不是打印机和打印程序的问题。然后我试着画个矩形,看看能不能打印出来,最终发现还是可以打印出来的。既然可以打印出矩形来,那么打印出图像来也应该不是问题,把图像中的每个像素用一个长和高为1的矩形画出来不就行了吗。根据这个思想,稍微改了下代码,点打印终于可以了,具体代码如下:

 1   int i;
 2     int j;
 3     for (i=0; i<m_image.GetWidth(); i++)
 4     {
 5         for (j=0; j<m_image.GetHeight(); j++)
 6         {
 7             byte *pByte = (byte *)m_image.GetPixelAddress(i, j);
 8             
 9             if (m_image.GetBPP() == 32) //确认该图像包含Alpha通道
10             {
11                 if (pByte[3])//确保透明背景不被画出
12                 {
13                     pDC->FillSolidRect(i,j,1,1,m_image.GetPixel(i,j));
14                 }
15                 
16             }
17             else
18             {
19                 pDC->FillSolidRect(i,j,1,1,m_image.GetPixel(i,j));
20             }
21         }
22     }

五、总结

  从这个小任务的完成,很多事情其实就是那么一点的思想,如果你对问题转变个思路,而不是一条道走到黑,问题也许一下就解决了。某个问题的解决方法不止一种,特别在计算机软件开发中。即使但你尝试了所有方法都解决不了一个问题的时候,也不要放弃,或许过片刻,或许过一天,一个新的idea就突然出现在你脑子里,把这个问题解决了。

原文地址:https://www.cnblogs.com/chengxuyuancc/p/3256420.html