【发布时间】:2021-06-05 23:42:58
【问题描述】:
我正在尝试显示动画 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, 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, 124, hWnd, NULL, NULL, 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);
}
这里是负责动画 GIF 的 GDIHelper.cpp 类的函数。
#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, UINT_PTR timer_id, 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, NULL, 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, m_iCurrentFrame);
SetTimer(hwnd, 120, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, NULL);
++m_iCurrentFrame;
InvalidateRect(hwnd, NULL, FALSE);
m_bIsPlaying = TRUE;
}
}
/** Function to draw the image in Window, must be call on WM_PAINT. **/
void GDIHelper::DrawItem(HDC hdc, int xPosition, int yPosition, int width, 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 的单帧,看起来计时器不起作用(我不确定),因为它没有动画。
【问题讨论】:
-
为什么你不确定定时器是否工作?你试过用调试器看看是不是?
-
在调试器下运行代码您还发现了什么?
isPlayable设置了吗?new和malloc的快乐组合是怎么回事?为什么有一个从未使用过的strGuid?为什么超过 1 帧的图像报告为ERROR:?为什么LoadImageFromFile会默默地忽略错误?为什么你相信你的评论Convert the string file_name to wstring?没有转换,该语句随机丢弃其输入。 -
我不确定问题是否出在计时器上,因为它会出现什么问题?函数
GDIHelper::OnTimer()在WndProc回调上被调用,但不是在静态控制回调上,这就是我不太确定的原因。这可能是一个逻辑错误,我不知道。 -
InitializeImage函数上的ERROR:实际上不是错误,我正在尝试检查是否正在调用这些函数,但我不小心输入了ERROR:而不是NOTICED:没有包括在该助手上编写的所有代码。我刚刚发布了我正在使用的功能。 -
WM_TIMER消息发布到传递给SetTimer调用的窗口。这是代码中静态控件的父窗口。
标签: c++ winapi visual-studio-2019 gdi+