vc6.0中用GDIPlus实现加载动态gif图片(非MFC实现)

 

今天心情很嗨皮,原因是花了5块钱买了一张刮刮彩,给中了100快,喜悦感可谓是油然而生吧,好了进入正题吧。关于GDI+的介绍在这里就不谈了,总之相比GDI确实方便了太多,比如首先GDI仅仅支持bmp格式的位图,对其他格式比如jpg,png,gif.....都很吃力了,而用GDI+确全部支持,而且用起来十分顺手,比如放置一幅jpg图片:

Image         image(L"Texture1.jpg");
graphics.DrawImage(&image, 0, 0, image.GetWidth(), image.GetHeight());


当然还需要构造一个graphics对象,也很方便:

Graphics graphics(hdc);

Microsoft Windows GDI+包含大约40个类、50个枚举和6个结构体。同时也有少数几个函数不属于任何类。Graphics类是整个GDI+接口的核心;它是实际进行线条、曲线、图形、图象和文本绘制的类。

多数类和Graphics类配合使用。例如,Graphics::DrawLine方法接受一个点传给一个Pen对象,该对象保存了即将绘制的线条的属性(颜色、宽度、虚线类型及其他)。Graphics::FillRectangle方法接受一个点传给LinearGradientBrush对象,该对象协同Graphics对象实现矩形的渐变色填充。Font和StringFormat对象影响到Graphics对象绘制文本的方式。Matrix对象用于存储和生成一个Graphics对象的世界变换矩阵,用于旋转、缩放和翻转图象。

至于想要知道更具体的就去找你度娘跟谷歌吧,初次接触GDI+让我兴奋的是他的线性渐变跟路径渐变,图像旋转图像平移,用这些弄出来的画面十分好看的,而且操作也十分容易。。。。。。。拍马屁的话不多说了,也不是我的风格,大家接触了就明了了。

说到支持gif,我赶紧就去测试了一把,就把上面代码中的jpg换做gif就OK了,如果gif是静态的,那完全满足要求,如果是动态的,那运行结果有点失望,现实的是静态的,说准确点就是该gif图片的第一个帧,哪如何加载动态gif呢,首先想到的是分析gif格式,网上也能够找到一大堆资料,不过看着还蛮费劲的,看过之后分析,知道gif实际上是有很多歌帧组成的,我们可以一个接一个的来显示出来,从而整体看起来就是一个符合要求的结果。

首先判断gif有多少个帧,用到如下几个API:

Image *image=new Image(L"123.gif");
  UINT count=0;
  count=image->GetFrameDimensionsCount();
  GUID *pDimensionIDs=(GUID*)new GUID[count];
  image->GetFrameDimensionsList(pDimensionIDs,count);
  WCHAR strGuid[39];
  StringFromGUID2(pDimensionIDs[0],strGuid,39);
  UINT frameCount=image->GetFrameCount(&pDimensionIDs[0]);
  delete []pDimensionIDs;


得到帧的数量之后,其实根据数量我们也能判断出来这个gif是否是动态的,是否大于1就是了,数量判断完毕接着就该判断帧的时间了吧,所谓的延迟。

// 假设图像具有属性条目 PropertyItemEquipMake. 
// 获取此条目的大小. 
int nSize = GetPropertyItemSize(PropertyTagFrameDelay); // 为属性条目分配空间. 
m_pPropertyItem = (PropertyItem*) malloc(nSize); GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem); 

 m_pPropertyItem->value 是一个长整形数组, 每个长整形代表每帧的延时。由于获取的属性不同,GetPropertyItem会获得不同大小的对象, 因此要由用户来获得的对象大小,开辟与删除 GetPropertyItem相关的内存。对象的大小是通过GetPropertyItemSize 获取的,其参数是你所感兴趣的属性条目。 一旦获取了帧的数量与延时,我们就可生成一个线程来调用 DrawFrameGIF()来显示。


起初我没有用到线程,我认为用线程不好控制(个人对线程的应用少之又少),反倒是写了一个单独的函数放到了WM_PAINT中,大概是这样的:

void showimage(HDC hdc)
{
Image *image=new Image(L"123.gif");
  UINT count=0;
  count=image->GetFrameDimensionsCount();
  GUID *pDimensionIDs=(GUID*)new GUID[count];
  image->GetFrameDimensionsList(pDimensionIDs,count);
  WCHAR strGuid[39];
  StringFromGUID2(pDimensionIDs[0],strGuid,39);
  UINT frameCount=image->GetFrameCount(&pDimensionIDs[0]);
  delete []pDimensionIDs;
  int size=image->GetPropertyItemSize(PropertyTagFrameDelay);
  PropertyItem* pItem=NULL;
  pItem=(PropertyItem*)malloc(size);
  image->GetPropertyItem(PropertyTagFrameDelay,size,pItem);
  UINT fcount=0;
  GUID Guid=FrameDimensionTime;
  while(true)
  {
    Graphics graphics(hdc);
    graphics.DrawImage(image,0,0,image->GetWidth(),image->GetHeight());
    image->SelectActiveFrame(&Guid,fcount++);
    if(fcount==frameCount)
      fcount=0;
    long lPause=((long*)pItem->value)[fcount]*10;
    Sleep(lPause);
  }
}


放到了WM_PAINT消息中,但是运行发现动态gif确实显示出来了,但是当程序窗口失去焦点进而发生重绘的时候发现图片就不会动了,最小最大化也是如此,可能就是那个死循环从中作怪吧,此时又想到了多线程来完成,于是改动了一下代码,应该是这样的:

void __cdecl showimage(LPVOID)
{
  HDC hdc=GetDC(g_hWnd);
  Image *image=new Image(L"123.gif");
  UINT count=0;
  count=image->GetFrameDimensionsCount();
  GUID *pDimensionIDs=(GUID*)new GUID[count];
  image->GetFrameDimensionsList(pDimensionIDs,count);
  WCHAR strGuid[39];
  StringFromGUID2(pDimensionIDs[0],strGuid,39);
  UINT frameCount=image->GetFrameCount(&pDimensionIDs[0]);
  delete []pDimensionIDs;
  int size=image->GetPropertyItemSize(PropertyTagFrameDelay);
  PropertyItem* pItem=NULL;
  pItem=(PropertyItem*)malloc(size);
  image->GetPropertyItem(PropertyTagFrameDelay,size,pItem);
  UINT fcount=0;
  GUID Guid=FrameDimensionTime;
  while(true)
  {
    Graphics graphics(hdc);
    graphics.DrawImage(image,0,0,image->GetWidth(),image->GetHeight());
    image->SelectActiveFrame(&Guid,fcount++);
    if(fcount==frameCount)
      fcount=0;
    long lPause=((long*)pItem->value)[fcount]*10;
    Sleep(lPause);
  }
  ReleaseDC(g_hWnd,hdc);
}


 

case WM_PAINT:
      hdc = BeginPaint(hWnd, &ps);
      // TODO: Add any drawing code here...
      RECT rt;
      GetClientRect(hWnd, &rt);
      _beginthread(showimage,0,0);
      //showimage(hdc);
      EndPaint(hWnd, &ps);
      break;

运行发现此时上述的那个重绘时遇到的图片假死已经不复存在了,这个时候表明看起来没什么问题了,但是仔细看发现gif图片交换的频率好像快了不少(跟原来的123.gif进行了肉眼对比),显的跟原版图片还是有点差距的,总之是不太理想,原因大致查了一下,可能是线程还没执行玩就 EndPaint(hWnd, &ps);了

最终跟网友交流方法,说到了定时器的思路,大体一想,确实定时器可以做到,思路:

开一个定时器。然后取gif当前帧到下一帧的时间间隔,做为定时器的间隔(最小100ms)。(定时器只用一次)。每次定时器到了。就把活动帧设到下一帧,然后触发窗口重绘。
剩下的。你只需要在wm_paint里面,把gif的当前帧画出来就好了.
大概流程:
1.取帧间隔,开定时器。
2.定时器到。杀掉定时器,选中下一帧,再取帧间隔。
3.触发窗口重绘。

按照这个思路终于算是比较完美的实现了gif动态图片的加载,主要代码大致这样的:

//定义2个ID
#define TIMER_FIR 1
#define TIMER_SEC 2
//核心工作
case WM_CREATE:
    SetTimer(hWnd,TIMER_FIR,0,NULL);
    break;
  case WM_TIMER:
    {
      switch(wParam)
      {
  case TIMER_FIR:
    {
    hdc=GetDC(hWnd);
  image=new Image(L"123.gif");
 count=0;
  count=image->GetFrameDimensionsCount();
pDimensionIDs=(GUID*)new GUID[count];
  image->GetFrameDimensionsList(pDimensionIDs,count);
  StringFromGUID2(pDimensionIDs[0],strGuid,39);
frameCount=image->GetFrameCount(&pDimensionIDs[0]);
  delete []pDimensionIDs;
 size=image->GetPropertyItemSize(PropertyTagFrameDelay);
 //PropertyItem* pItem=NULL;
 // pItem=(PropertyItem*)malloc(size);
  pItem=(PropertyItem*)new PropertyItem[size];
  image->GetPropertyItem(PropertyTagFrameDelay,size,pItem);
fcount=0;
Guid=FrameDimensionTime;

Graphics  graphics(hdc);
   graphics.DrawImage(image,0,0);
    image->SelectActiveFrame(&Guid,fcount++);
    if(fcount==frameCount)
      fcount=0;
 lPause=((long*)pItem->value)[fcount]*10;
    //ReleaseDC(hWnd,hdc);
    KillTimer (hWnd, TIMER_FIR) ;
    SetTimer(hWnd,TIMER_SEC,lPause,NULL);
        InvalidateRect (hWnd, NULL, FALSE) ;
    break;
    }
  case TIMER_SEC:
    {
    image->SelectActiveFrame(&Guid,fcount++);
    if(fcount==frameCount)
      fcount=0;
   lPause=((long*)pItem->value)[fcount]*10;
  KillTimer(hWnd,TIMER_SEC);
  SetTimer(hWnd,TIMER_SEC,lPause,NULL);
  InvalidateRect (hWnd, NULL, FALSE) ;
    }
      }
    }

主要是分享一下这个问题的解决思路,免得后面的人又绕弯路,这样就不太好了。

贴一张运行效果图:


完整的工程已经上传到了CSDN资源区了,但是不晓得什么原因现在还找不到地址,稍后会附上附近的下载地址(等CSDN的消息吧)。最后欢迎大家交流学习。

你可能感兴趣的:(windows,SDK/MFC)