问题描述
我正在尝试显示动画 GIF,但我设法让它工作了。但是,我有一个问题,其他绘制的图像和文本受到计时器的影响,它们正在重新绘制。我尝试使用 FillRect()
解决它,但它们在闪烁。
所以我尝试使用这个 Static Control,您可以在其中创建子类并在其上绘画。
WNDPROC StaticWndProc = NULL;
GDIHelper gdiHelper;
LRESULT CALLBACK MyStaticWndProc2(HWND hwnd,UINT Message,WParaM wparam,LParaM lparam) { //Call back for static control.
switch(Message) {
case WM_TIMER:{
gdiHelper.OnTimer(); // Do something on timer.
return 0;
}
case WM_PAINT:{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd,&ps);
gdiHelper.DrawItem(hdc,15,95,95); //draw the GIF image.
EndPaint(hwnd,&ps);
return TRUE;
}
case WM_DESTROY:{
gdiHelper.Desrtroy();
return 0;
}
}
return CallWindowProc(StaticWndProc,hwnd,Message,wparam,lparam); //v2
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WParaM wParam,LParaM lParam) {
switch(message) {
case WM_CREATE: {
HWND staticcontrol = CreateWindowEx(0,L"STATIC",NULL,WS_CHILD | WS_VISIBLE,150,200,124,hWnd,NULL); //create the static control.
StaticWndProc = (WNDPROC)SetwindowLongPtr(staticcontrol,GWLP_WNDPROC,(LParaM)MyStaticWndProc2); //subclass the static control.
gdiHelper.LoadImageFromFile(hWnd,ID_OF_YOUR_TIMER,"C:\\spinner.gif",L"GIF"); //load the GIF.
break;
}
case WM_PAINT:{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hWnd,&ps);
//paint other images and text here...
EndPaint(hWnd,&ps);
break;
}
case WM_DESTROY:{
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hWnd,message,wParam,lParam);
}
以下是 GDIHelper.cpp
类负责为 GIF 制作动画的函数。
#include "GDIHelper.h"
/**
GDIHelper.h has the following declarations;
public:
GDIHelper();
void LoadImageFromresource(HWND hWnd,UINT_PTR timer_id,HMODULE hMod,const wchar_t* resid,const wchar_t* restype);
void LoadImageFromFile(HWND hWnd,string file_location,const wchar_t* restype);
void OnTimer();
void Desrtroy();
void Stop(UINT_PTR timer_id);
void DrawItem(HDC hdc,int xPosition,int yPosition,int width,int height);
private:
void InitializeImage();
bool IsFileExist(string file_name);
void AnimateGIF();
HWND hwnd;
Image* m_pImage;
GUID* m_pDimensionIDs;
UINT m_FrameCount;
PropertyItem* m_pItem;
UINT m_iCurrentFrame;
UINT_PTR timer_id;
BOOL m_bIsPlaying;
BOOL isPlayable;
**/
/** GDIHelper is a class helper to display images and animated GIF **/
GDIHelper::GDIHelper() {
timer_id = 0;
m_FrameCount = 0;
m_iCurrentFrame = 0;
m_pImage = NULL;
m_pDimensionIDs = NULL;
m_pItem = NULL;
hwnd = NULL;
m_bIsPlaying = FALSE;
isPlayable = FALSE;
}
/** Function to destroy objects and arrays,call this function on WM_DESTROY of WinProc. **/
void GDIHelper::Desrtroy() {
if(m_pDimensionIDs) {
delete[] m_pDimensionIDs;
}
if(m_pItem) {
free(m_pItem);
}
if(m_pImage) {
delete m_pImage;
}
}
/** Functon to load the next frame of GIF,must be call on WM_TIMER. **/
void GDIHelper::OnTimer() {
if(isPlayable) {
KillTimer(hwnd,timer_id);
GUID Guid = FrameDimensionTime;
m_pImage->SelectActiveFrame(&Guid,m_iCurrentFrame);
SetTimer(hwnd,120,((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10,NULL);
m_iCurrentFrame = (++m_iCurrentFrame) % m_FrameCount;
InvalidateRect(hwnd,FALSE);
}
}
/** Private function,call this to animate the GIF image,should be call before drawing the image usually on WM_PAINT. **/
void GDIHelper::AnimateGIF() {
if(m_bIsPlaying == TRUE) {
return;
}
if(isPlayable) {
m_iCurrentFrame = 0;
GUID Guid = FrameDimensionTime;
m_pImage->SelectActiveFrame(&Guid,NULL);
++m_iCurrentFrame;
InvalidateRect(hwnd,FALSE);
m_bIsPlaying = TRUE;
}
}
/** Function to draw the image in Window,must be call on WM_PAINT. **/
void GDIHelper::DrawItem(HDC hdc,int height) {
AnimateGIF(); //This will only works if the image has more than one frame.
Graphics g(hdc);
g.DrawImage(m_pImage,xPosition,yPosition,width,height);
}
/** Private function,accessible only in this class,check if file exist. **/
bool GDIHelper::IsFileExist(string file_name) {
struct stat buffer;
return (stat(file_name.c_str(),&buffer) == 0);
}
/** Private function,function to count and get the frame of image. **/
void GDIHelper::InitializeImage() {
UINT count = m_pImage->GetFrameDimensionsCount();
m_pDimensionIDs = new GUID[count];
m_pImage->GetFrameDimensionsList(m_pDimensionIDs,count);
m_FrameCount = m_pImage->GetFrameCount(&m_pDimensionIDs[0]);
if(m_FrameCount > 1) { //frame of GIF is more than one,all good,we don't want the error of `Access violation reading location`
isPlayable = TRUE;
OutputDebugString(_T("NOTICED: GDIHelper::InitializeImage >> Image file has more than 1 frame,its playable.\n"));
}
UINT TotalBuffer = m_pImage->GetPropertyItemSize(PropertyTagFrameDelay);
m_pItem = (PropertyItem*)malloc(TotalBuffer);
m_pImage->GetPropertyItem(PropertyTagFrameDelay,TotalBuffer,m_pItem);
}
/** Function to Load Image from Local File. **/
void GDIHelper::LoadImageFromFile(HWND hWnd,UINT_PTR ttimer_id,string file_name,const wchar_t* restype) {
hwnd = hWnd;
timer_id = ttimer_id;
if(!IsFileExist(file_name)) {
OutputDebugString(_T("ERROR: GDIHelper::LoadImageFromFile >> Invalid file or not exist\n"));
return;
}
std::wstring widestr = std::wstring(file_name.begin(),file_name.end()); // Convert the string file_name to wstring.
m_pImage = Image::FromFile(widestr.c_str()); //Convert the wtring to wchar and initialize.
InitializeImage(); //Initialize the image.
}
只绘制了 GIF 的单帧,看起来计时器不起作用(我不确定),因为它没有动画。
解决方法
您将错误的句柄传递给窗口。在您的 WndProc 回调中,您需要传递您创建的 hWnd
而不是 staticcontrol
。
应该是;
case WM_CREATE: {
HWND staticcontrol = CreateWindowEx(0,L"STATIC",NULL,WS_CHILD | WS_VISIBLE,150,200,124,hWnd,NULL); //create the static control.
StaticWndProc = (WNDPROC)SetWindowLongPtr(staticcontrol,GWLP_WNDPROC,(LPARAM)MyStaticWndProc2); //subclass the static control.
/** Instead of `hWnd`,the handle of the parent Window,you need to use the handle window you created for static control which is `staticcontrol` **/
gdiHelper.LoadImageFromFile(staticcontrol,ID_OF_YOUR_TIMER,"C:\\spinner.gif",L"GIF"); //load the GIF.
break;
}
除了 WndProc 回调中的 WM_DESTROY
之外,您可能还想这样做;
case WM_DESTROY:{
SetWindowLong(staticcontrol,GWL_WNDPROC,(LPARAM)MyStaticWndProc2);
PostQuitMessage(0);
break;
}