您的位置:首页 > 其它

关于携带完整alpha通道图标的技术研究

2013-03-20 17:27 253 查看
文章来源:http://tech.chinaunix.net/a2009/1103/803/000000803022_1.shtml

#include "StdAfx.h"

  #include "AlphaIcon.h"

  /*加载位图,注意,只能加载bpp=24的位图*/

  bool CAlphaIcon::Load(TCHAR *filename)

  {

  FILE* stream;

  int i,j;

  size_t bytesRead=0, stride=0;

  /*必须是long型(即int32)*/

  long offset;

  BITMAPFILEHEADER fileHeader;

  BITMAPINFOHEADER infoHeader;

  //是否加载过图片

  if(this->m_data.pPixels != NULL)

  this->DeleteBitmap();

  stream=t_fopen(filename,_T("rb"));

  if(stream==NULL)

  {

  //open file failed

  return false;

  }

  fseek(stream,0,0);

  fread(&fileHeader,sizeof(fileHeader),1,stream);

  fread(&infoHeader,sizeof(infoHeader),1,stream);

  /*设置图片的宽度和高度信息*/

  this->m_data.width=(unsigned int)(infoHeader.biWidth);

  this->m_data.height=(unsigned int)(infoHeader.biHeight);

  this->m_data.bpp = (BYTE)infoHeader.biBitCount;//位深度

  /* stride: scan line bytes count. padding for 4 bytes */

  /* stride:扫描行宽度 */

  this->m_data.stride=(infoHeader.biBitCount * infoHeader.biWidth+31)/32*4;

  this->m_data.strideAlpha = (8* infoHeader.biWidth + 31)/32*4;

  /*分配数据空间!*/

  this->m_data.pPixels=(BYTE*)malloc( this->m_data.stride * infoHeader.biHeight );

  if(this->m_data.pPixels==NULL) /*检测内存是否分配成功!*/

  {

  fclose(stream);

  return false;

  }

  this->m_data.pAlpha = (BYTE*)malloc(this->m_data.strideAlpha * infoHeader.biHeight );

  if(this->m_data.pAlpha==NULL) /*检测内存是否分配成功!*/

  {

  free(this->m_data.pPixels);

  fclose(stream);

  return false;

  }

  //加载位图数据

  fseek(stream, fileHeader.bfOffBits, SEEK_SET);

  bytesRead = fread(this->m_data.pPixels, 1, this->m_data.stride * this->m_data.height, stream);

  //加载alpha通道

  bytesRead = fread(this->m_data.pAlpha, 1, this->m_data.strideAlpha * this->m_data.height, stream);

  fclose(stream); /* close the bitmap file */

  //设置bminfo

  memcpy(&this->m_bminfo.bmiHeader, &infoHeader, sizeof(BITMAPINFOHEADER));

  //打开内存映射

  this->m_data.hSection = CreateFileMapping(

  INVALID_HANDLE_VALUE, // use paging file

  NULL, // default security

  PAGE_READWRITE, // read/write access

  0, // max. object size

  this->m_data.stride * this->m_data.height,// buffer size

  NULL); // name of mapping object

  this->m_data.lpDIBData = (BYTE*)MapViewOfFile(

  this->m_data.hSection,

  FILE_MAP_ALL_ACCESS,

  0,

  0, //文件偏移地址

  0); //If dwNumberOfBytesToMap is zero, the entire file is mapped.

  return true;/*加载成功!返回*/

  }

  /*释放位图数据占用的内存*/

  void CAlphaIcon::DeleteBitmap()

  {

  /*释放内存!*/

  if(this->m_data.pPixels != NULL)

  free(this->m_data.pPixels);

  if(this->m_data.pAlpha != NULL)

  free(this->m_data.pAlpha);

  //关闭内存映射

  if(this->m_data.lpDIBData!=NULL)

  UnmapViewOfFile(this->m_data.lpDIBData);

  if(this->m_data.hSection != NULL)

  CloseHandle(this->m_data.hSection);

  this->m_data.pPixels = NULL;

  this->m_data.pAlpha = NULL;

  this->m_data.lpDIBData = NULL;

  this->m_data.hSection = NULL;

  return;

  }

  //把图片绘制到HDC

  void CAlphaIcon::Draw(HDC hDC, int nXDest, int nYDest)

  {

  int i,j;

  //是否已经分配了空间

  if(this->m_data.pPixels == NULL) return;

  LPVOID lpBits;//接收数据起始地址

  HBITMAP hDIB = CreateDIBSection(hDC, &this->m_bminfo, DIB_PAL_COLORS, &lpBits, this->m_data.hSection, 0);

  HDC hmemdc = CreateCompatibleDC(hDC);

  HGDIOBJ hOldBm = SelectObject(hmemdc, hDIB);

  //贴图

  BitBlt(hmemdc, 0, 0, this->m_data.width, this->m_data.height, hDC, nXDest, nYDest, SRCCOPY);

  //依次对每个像素进行alpha合成

  for(i=0; i< this->m_data.stride * this->m_data.height; i++)

  {

  this->m_data.lpDIBData[i] = BLEND(

  this->m_data.lpDIBData[i], //背景像素值

  this->m_data.pPixels[i], //上层像素值

  this->m_data.pAlpha[i/3] //alpha

  );

  }

  BitBlt(hDC, nXDest, nYDest, this->m_data.width, this->m_data.height, hmemdc, 0, 0, SRCCOPY);

  //清理

  SelectObject(hmemdc, hOldBm);

  DeleteDC(hmemdc);

  DeleteObject(hDIB);

  }

  // 获取位图尺寸

  void CAlphaIcon::GetSize(SIZE* sz)

  {

  if(this->m_data.pPixels == NULL || sz==NULL)

  {

  return;

  }

  sz->cx = this->m_data.width;

  sz->cy = this->m_data.height;

  return;

  }

  //生成一个特殊图标文件,注意文件bmfile就会被改写!

  //bmfile: 普通位图文件

  //alfile: alpha通道位图

  bool CAlphaIcon::WriteIconFile(TCHAR* bmfile, TCHAR* alfile)

  {

  TCHAR path[_MAX_PATH];

  TCHAR drive[_MAX_DRIVE];

  TCHAR dir[_MAX_DIR];

  TCHAR fname[_MAX_FNAME], backupfname[_MAX_FNAME];

  TCHAR ext[_MAX_EXT];

  FILE *stream1, *stream2;

  int i,j;

  size_t bytesRead=0, stride=0;

  /*必须是long型(即int32)*/

  long offset;

  BITMAPFILEHEADER fileHeader;

  BITMAPINFOHEADER infoHeader;

  //是否加载过图片

  if(this->m_data.pPixels != NULL)

  this->DeleteBitmap();

  //对bmp文件进行追加alpha通道的内容

  stream1=t_fopen(bmfile, _T("ab+"));

  if(stream1==NULL)

  {

  //open file failed

  return false;

  }

  stream2=t_fopen(alfile,_T("rb"));

  if(stream2==NULL)

  {

  //open file failed

  fclose(stream1);

  return false;

  }

  //此时,对原来的位图进行以下备份,原位图拷贝到 FILENAME_backup.bmp

  t_splitpath(bmfile, drive, dir, fname, ext);

  t_sprintf(backupfname, _T("%s_backup"), fname);

  t_makepath(path, drive, dir, backupfname, ext);

  CopyFile(bmfile, path, FALSE); //覆盖写

  fread(&fileHeader,sizeof(fileHeader),1,stream2);

  fread(&infoHeader,sizeof(infoHeader),1,stream2);

  /* stride: scan line bytes count. padding for 4 bytes */

  /* stride:扫描行宽度 */

  stride =(infoHeader.biBitCount * infoHeader.biWidth+31)/32*4;

  /*分配数据空间!*/

  BYTE *buffer = (BYTE*)malloc(stride * infoHeader.biHeight);

  if(buffer==NULL) /*检测内存是否分配成功!*/

  {

  fclose(stream1);

  fclose(stream2);

  return false;

  }

  //加载alpha通道数据

  fseek(stream2, fileHeader.bfOffBits, SEEK_SET);

  bytesRead = fread(buffer, 1, stride * infoHeader.biHeight, stream2);

  //追加到文件1尾部

  fwrite(buffer, 1, stride * infoHeader.biHeight, stream1);

  //清理

  fclose(stream1);

  fclose(stream2); /* close the bitmap file */

  free(buffer);

  //把追加好的文件拷走成为 FILENAME_ICON.bmp

  t_sprintf(backupfname, _T("%s_ICON"), fname);

  t_makepath(path, drive, dir, backupfname, ext);

  CopyFile(bmfile, path, FALSE); //覆盖写

  //把备份文件拷回成原来的文件名 FILENAME_backup.bmp -> FILENAME.bmp

  t_sprintf(backupfname, _T("%s_backup"), fname);

  t_makepath(path, drive, dir, backupfname, ext);

  CopyFile(path, bmfile, FALSE); //覆盖写

  return true;/*成功!返回*/

  }

  //用于判断已经是否加载了图片

  bool CAlphaIcon::IsNull()

  {

  return (this->m_data.pPixels == NULL);

  }

  这样我们在绘制图标时,先通过加载方法从文件中读取图像数据,然后在绘制时,把 HDC 参数和起始位置信息传递给 这个类的 Draw 方法即可完成 alpha 合成。由于这种图标文件在现实中不可能存在,所以我还在关于对话框上做了一些简单功能,用于生成这样的携带alpha 通道数据的位图文件。我在项目的“关于对话框”修改如下所示:

  


  在上面半部分是原本的项目框架生成的关于对话框,在下半部分,主要是用于选择用于制作图标的文件。第一个文件是用于显示的原始 RGB 图像,第二个文件是用于追加到文件尾部去的alpha 通道位图文件,它应该是一个普通的灰度图像,和第一个图像的大小完全相同。选择后点击“改写BMP文件”按钮,即可生成一个带有alpha 通道的图标文件。点击后弹出是否成功的提示消息框。如果两个文件都是存在的,则通常是成功的,我并没有对文件本身内容采取更多的校验。

  例如,输入的文件名分别是 icon01.bmp, icon01_alpha.bmp; 则生成的图标文件命名是 icon01_ICON.bmp,同时我还拷贝了一个原图的备份文件(icon01_backup.bmp)。

  由于编写匆忙,demo程序的提示信息并不明确,因此这里简单再介绍以下:

  (1)在demo 程序中,通过帮助->关于打开“关于对话框”去制作 带有alpha通道的图标。

  (2)在文件->选择背景位图菜单,可以打开一个图像文件,作为程序窗口的背景图。通过选择“特殊图标”菜单,可以打开一个由步骤(1)生成的图标文件。同时,可以用鼠标按下去拖曳图标,可以看到图标在背景图上不同位置的合成效果。由于实时刷新会有很强烈的闪烁感,所以为了避免闪烁,我把实时刷新的方式改为了通过绘制焦点矩形的反馈方式。

  下面是这个程序的截图:

  


  结论:

  和现在图标的绘制方式相比,应当说处理量是增大的。对于对效率要求更高的场合比如游戏等,需要适当兼顾性能和视觉效果的平衡。

  最后,这里是相关范例的源代码压缩包:

  http://files.cnblogs.com/hoodlum1980/JRL_AlphaIconDemo.rar
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: