I am trying to display a GIF picture as splash as starting of my small program in visual studio. I am really getting crazy. I looked it is possible in Qt IDE but I really need it in visual studio because my other code works only with visual studio. And yes I tried to convert my code for Qt giving me too many errors.
I have seen this post.
I am using GDI+ but still dunno how to simply display it instead of play and stop. It's okay even instead of splash to display a small form that plays the GIF file, can you guys give me a small code snippet in how to do it in c++?
Thanks.
Here is an MFC window class that implements a splash screen using GDI Plus for displaying an (animated) GIF.
I've encapsulated everything inside a header file to simplify using it with your project. Save it as an .h file (maybe "SplashWnd.h"), then include it wherever you want to set up the splash. You app's InitInstance might be a good place to add it - something like this line (before any DoModal calls):
SplashWnd splash(_T("Filname.gif"));
The constructor can also take parameters for controlling the delay before auto-closing, and also specifying a function to call when the splash is closed.
The splash window has no border or caption - it appears only as the loaded image, floating on top of any other windows. Its icon doesn't appear in the taskbar, and will close when its timeout expires, or the user clicks the window or presses a key.
#pragma once
#include <functional>
#include <afxwin.h>
#include <gdiplus.h>
#pragma comment(lib,"gdiplus.lib")
inline void ManageGdiPlusInit(bool release=false) {
static int refcount = 0;
static ULONG_PTR token;
if(release) {
if(--refcount == 0) {
Gdiplus::GdiplusShutdown(token);
}
} else if(++refcount == 1) {
Gdiplus::GdiplusStartupInput startup_input;
Gdiplus::GdiplusStartup(&token, &startup_input, 0);
} }
inline void GdiPlusInit() { ManageGdiPlusInit(false); }
inline void GdiPlusRelease() { ManageGdiPlusInit(true); }
namespace {
class SplashWnd : public CWnd {
protected:
static CString WindowClass() {
static CString name;
if(name.IsEmpty()) {
name = AfxRegisterWndClass(CS_DROPSHADOW, 0, (HBRUSH)GetStockObject(GRAY_BRUSH), 0);
}
return name;
}
Gdiplus::Image *m_pImage;
UINT m_FrameCount;
unsigned char *m_FrameDelayData;
const UINT *m_FrameDelays;
UINT m_CurFrameIndex;
UINT m_AnimationTimerId;
UINT m_ExpireTimerId;
CRect m_WindowRect;
std::function<void()> m_DismissCallback;
DECLARE_MESSAGE_MAP()
afx_msg void OnLButtonDown(UINT nFlags, CPoint point) {
DestroyWindow();
}
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
DestroyWindow();
}
afx_msg void OnDestroy() {
if(m_AnimationTimerId != UINT(-1)) {
KillTimer(m_AnimationTimerId);
}
if(m_ExpireTimerId != UINT(-1)) {
KillTimer(m_ExpireTimerId);
}
if(m_DismissCallback) {
m_DismissCallback();
}
CWnd::OnDestroy();
}
afx_msg void OnTimer(UINT nIDEvent) {
if(nIDEvent == m_AnimationTimerId) {
if(++m_CurFrameIndex >= m_FrameCount) {
m_CurFrameIndex = 0;
}
DrawCurFrame();
KillTimer(m_AnimationTimerId);
m_AnimationTimerId = SetTimer(1, m_FrameDelays[m_CurFrameIndex], 0);
return;
}
if(nIDEvent == m_ExpireTimerId) {
DestroyWindow();
return;
} }
void PostNcDestroy() {
if(m_DeleteSelf) {
delete this;
}
}
void DrawCurFrame() {
Gdiplus::Graphics g(m_hWnd);
GUID dim_select_id = Gdiplus::FrameDimensionTime;
m_pImage->SelectActiveFrame(&dim_select_id, m_CurFrameIndex);
g.DrawImage(m_pImage, 0, 0, m_WindowRect.Width(), m_WindowRect.Height());
}
public:
// set m_DeleteSelf to true if a SplashWnd is created with new, and you want it to
// auto-delete itself when the window expires or is dismissed.
bool m_DeleteSelf;
// file_path the gif file path
// ExpireMs the time, in milliseconds until the window automatically closes itself
// WidthFactor the fraction of the width of the primary display to use as the splash screen's width
// HeightFactor the fraction of the height of the primary display to use as the height
// If WidthFactor or HeightFactor are 0, the original image aspect ratio is preserved
// If both are 0, the original image size, in pixels is used
SplashWnd(CString file_path, DWORD ExpireMs=2000, double WidthFactor=0.4, double HeightFactor=0) {
GdiPlusInit();
m_pImage = new Gdiplus::Image(file_path);
// Set up an array of frame times for animated images
UINT dimension_count = m_pImage->GetFrameDimensionsCount();
GUID dimension_id;
m_pImage->GetFrameDimensionsList(&dimension_id, 1);
m_FrameCount = m_pImage->GetFrameCount(&dimension_id);
UINT frame_delay_size = m_pImage->GetPropertyItemSize(PropertyTagFrameDelay);
m_FrameDelayData = new unsigned char[frame_delay_size];
Gdiplus::PropertyItem* frame_delay_item = reinterpret_cast<Gdiplus::PropertyItem*>(m_FrameDelayData);
m_pImage->GetPropertyItem(PropertyTagFrameDelay, frame_delay_size, frame_delay_item);
m_FrameDelays = reinterpret_cast<const UINT*>(frame_delay_item->value);
// Figure out the size and location of the splash window
int primary_width = GetSystemMetrics(SM_CXFULLSCREEN);
int primary_height = GetSystemMetrics(SM_CYFULLSCREEN);
int splash_width = int(primary_width * WidthFactor);
int splash_height = int(primary_height * HeightFactor);
if(splash_width == 0) {
if(splash_height == 0) {
splash_width = m_pImage->GetWidth();
splash_height = m_pImage->GetHeight();
} else {
splash_width = primary_width * splash_height / primary_height;
}
} else if(splash_height == 0) {
splash_height = primary_height * splash_width / primary_width;
}
int l = (primary_width - splash_width) / 2;
int t = (primary_height - splash_height) / 2;
int r = l + splash_width;
int b = t + splash_height;
m_WindowRect.SetRect(
(primary_width - splash_width) / 2,
(primary_height - splash_height) / 2,
(primary_width + splash_width) / 2,
(primary_height + splash_height) / 2);
// WS_EX_TOPMOST makes the window cover up other, regular windows
// WS_EX_TOOLWINDOW prevents an icon for this window in the taskbar
// WS_POPUP prevents caption and border from being drawn
CreateEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, WindowClass(), _T("Splash"), WS_VISIBLE | WS_POPUP, m_WindowRect, 0, 0);
// Show the first frame
m_CurFrameIndex = 0;
DrawCurFrame();
// Set up the frame-flipping animation timer
m_ExpireTimerId = m_AnimationTimerId = UINT(-1);
if(m_FrameCount > 1) {
m_AnimationTimerId = SetTimer(1, m_FrameDelays[m_CurFrameIndex], 0);
}
// Set up the expiration timer
if(ExpireMs != INFINITE) {
m_ExpireTimerId = SetTimer(2, ExpireMs, 0);
}
m_DeleteSelf = false;
}
// Constructor which takes a callback function which will be called when the splash window closes
template <typename F>
SplashWnd(CString file_path, DWORD ExpireMs, double WidthFactor, double HeightFactor, F DismissCallback)
: SplashWnd(file_path, ExpireMs, WidthFactor, HeightFactor)
{
m_DismissCallback = DismissCallback;
}
~SplashWnd() {
delete [] m_FrameDelayData;
delete m_pImage;
GdiPlusRelease();
}
};
// Message map, usually in an implementation file, but here encapsulated inside the header
// using an anonymous namespace to prevent possible ODR problems.
BEGIN_MESSAGE_MAP(SplashWnd, CWnd)
ON_WM_KEYDOWN()
ON_WM_LBUTTONDOWN()
ON_WM_TIMER()
ON_WM_DESTROY()
END_MESSAGE_MAP()
}
Related
At first I called the Create method of the CFrameWnd within another class.
Then I continued with the Create method of CDockablePane with the FrameWnd as the pParentWnd parameter.
The second Create was not successful, an assertion occured in the following code:
void CMFCDragFrameImpl::Init(CWnd* pDraggedWnd)
{
ASSERT_VALID(pDraggedWnd);
m_pDraggedWnd = pDraggedWnd;
CWnd* pDockSite = NULL;
if (m_pDraggedWnd->IsKindOf(RUNTIME_CLASS(CPaneFrameWnd)))
{
CPaneFrameWnd* pMiniFrame = DYNAMIC_DOWNCAST(CPaneFrameWnd, m_pDraggedWnd);
pDockSite = pMiniFrame->GetParent();
}
else if (m_pDraggedWnd->IsKindOf(RUNTIME_CLASS(CPane)))
{
CPane* pBar = DYNAMIC_DOWNCAST(CPane, m_pDraggedWnd);
ASSERT_VALID(pBar);
CPaneFrameWnd* pParentMiniFrame = pBar->GetParentMiniFrame();
if (pParentMiniFrame != NULL)
{
pDockSite = pParentMiniFrame->GetParent();
}
else
{
pDockSite = pBar->GetDockSiteFrameWnd();
}
}
m_pDockManager = afxGlobalUtils.GetDockingManager(pDockSite);
if (afxGlobalUtils.m_bDialogApp)
{
return;
}
ENSURE(m_pDockManager != NULL); <-----------------------
}
Somehow a docking manager seems to be missing. Is it possible that CFrameWnd is not suitable for CDockablePane? Or the docking manager needs to be initialized?
Thanks for your help (code snippets are welcome)!
To add a dockable pane to your project, the first step is to derive a new class from CDockablePane and you must add two message handlers for OnCreate and OnSize, and add a member child window as the main content. Your simple CTreePane class should look like this:
class CTreePane : public CDockablePane
{
DECLARE_MESSAGE_MAP()
DECLARE_DYNAMIC(CTreePane)
protected:
afx_msg int OnCreate(LPCREATESTRUCT lp);
afx_msg void OnSize(UINT nType,int cx,int cy);
private:
CTreeCtrl m_wndTree ;
};
int CTreePane::OnCreate(LPCREATESTRUCT lp)
{
if(CDockablePane::OnCreate(lp)==-1)
return -1;
DWORD style = TVS_HASLINES|TVS_HASBUTTONS|TVS_LINESATROOT|
WS_CHILD|WS_VISIBLE|TVS_SHOWSELALWAYS | TVS_FULLROWSELECT;
CRect dump(0,0,0,0) ;
if(!m_wndTree.Create(style,dump,this,IDC_TREECTRL))
return -1;
return 0;
}
In the OnSize handler, you should size your control to fill the entire dockable pane client area.
void CTreePane::OnSize(UINT nType,int cx,int cy)
{
CDockablePane::OnSize(nType,cx,cy);
m_wndTree.SetWindowPos(NULL,0,0,cx,cy, SWP_NOACTIVATE|SWP_NOZORDER);
}
To support a dockable pane in your frame, you must first derive from the Ex family of frames (CFrameWndEx, CMDIFrameWndEx, ..) and in the OnCreate handler, you should initialize the docking manager by setting the allowable docking area, general properties, smart docking mode, …etc.
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
CDockingManager::SetDockingMode(DT_SMART);
EnableAutoHidePanes(CBRS_ALIGN_ANY);
...
}void CMainFrame::OnTreePane()
{
if(m_treePane && m_treePane->GetSafeHwnd())
{
m_treePane->ShowPane(TRUE,FALSE,TRUE);
return ;
}
m_treePane = new CTreePane;
UINT style = WS_CHILD | CBRS_RIGHT |CBRS_FLOAT_MULTI;
CString strTitle = _T("Tree Pane");
if (!m_treePane->Create(strTitle, this,
CRect(0, 0, 200, 400),TRUE,IDC_TREE_PANE, style))
{
delete m_treePane;
m_treePane = NULL ;
return ;
}
m_treePane->EnableDocking(CBRS_ALIGN_ANY);
DockPane((CBasePane*)m_treePane,AFX_IDW_DOCKBAR_LEFT);
m_treePane->ShowPane(TRUE,FALSE,TRUE);
RecalcLayout();
}
Hy i have build with MFC a interface dialog Box Simply with one radio button.
like this:
CONTROL " Encode",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON | BS_RIGHT | BS_NOTIFY,35,20,41,10,WS_EX_TRANSPARENT in the RC file
i have create la class and with m_radio_0.SubclassDlgItem(IDC_RADIO1, this);.
I have writen my DrawItem. So I have a problem when i click on, i see the checkbow change but never stay check. And when i want getcheck() said 0.
My code is that on:
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CString strText;
int h = lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top;
CRect rect(lpDrawItemStruct->rcItem.left + 2,
lpDrawItemStruct->rcItem.top + 2,
lpDrawItemStruct->rcItem.left + h - 3,
lpDrawItemStruct->rcItem.top + h - 3);
// just draws focus rectangle when listbox is empty
int selChange = lpDrawItemStruct->itemAction & ODA_SELECT;
int focusChange = lpDrawItemStruct->itemAction & ODA_FOCUS;
int drawEntire = lpDrawItemStruct->itemAction & ODA_DRAWENTIRE;
BOOL sel = lpDrawItemStruct->itemState & ODS_SELECTED;
// Draws background rectangle, color depends on transparency
pDC->FillSolidRect(&lpDrawItemStruct->rcItem,::GetSysColor((GetExStyle()&WS_EX_TRANSPARENT) ? COLOR_BTNFACE : COLOR_WINDOW));
if (lpDrawItemStruct->itemState & ODS_SELECTED) //
{
pDC->DrawFrameControl( rect, DFC_BUTTON, DFCS_CHECKED | DFCS_BUTTONRADIO);
CButton::SetCheck(TRUE);
}
else
{
pDC->DrawFrameControl( rect, DFC_BUTTON, DFCS_BUTTONRADIO);
CButton::SetCheck(FALSE);
}
// Draws item text
::SetTextColor(lpDrawItemStruct->hDC, m_crTextColor);
// DEFAULT ==> BS_RIGHT
UINT Option = DT_RIGHT;
if ((uStyle & BS_RIGHT) == BS_RIGHT) Option = DT_RIGHT;
if ((uStyle & BS_LEFT) == BS_LEFT) Option = DT_LEFT;
if ((uStyle & BS_CENTER) == BS_CENTER) Option = DT_CENTER;
GetWindowText(strText);
::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(),
&lpDrawItemStruct->rcItem, Option);
::SetBkColor(lpDrawItemStruct->hDC, m_crBkColor);
>
Do have a solution for me.
Thanks
Just my response :
#include the header:
<<
// This file was created on March 28th 2001. By Robert Brault
//
//
#if !defined(AFX_COLORBUTTON_H__714C19E7_EA25_42DF_928A_51AC901B813D__INCLUDED_)
#define AFX_COLORBUTTON_H__714C19E7_EA25_42DF_928A_51AC901B813D__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// ColorRadio.h : header file
//
#include "color.h"
/////////////////////////////////////////////////////////////////////////////
// ColorRadio window
class CColorRadioButton : public CButton
{
// Construction
public:
void SetTextColor(COLORREF crColor); // This Function is to set the Color for the Text.
void SetBkColor(COLORREF crColor); // This Function is to set the BackGround Color for the Text.
void SetCheck(UINT Value);
UINT GetCheck();
CColorRadioButton();
void PreSubclassWindow();
virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/);
DWORD Mystyle;
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(ColorRadio)
//}}AFX_VIRTUAL
virtual ~CColorRadioButton();
// Generated message map functions
protected:
//{{AFX_MSG(CColorStatic)
CBrush m_brBkgnd; // Holds Brush Color for the Static Text
COLORREF m_crBkColor; // Holds the Background Color for the Text
COLORREF m_crTextColor; // Holds the Color for the Text
BOOL m_rb1Checked;
afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
UINT Value_Check;
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_COLORSTATIC_H__614C19E7_EA25_42DF_928A_51AC901B813D__INCLUDED_)
>>
and the class
<<
// This file was created on March 28th 2001 by Robert Brault.
// I created this Class to be able change the Color of your Static Text.
// This is Derived from CButton.
//
// There are three functions available Currently:
// SetBkColor(COLORREF crColor)
// SetTextColor(COLORREF crColor)
//
// How To Use:
// Add three files to your project
// CColorRadioButton.cpp, CColorRadioButton.h and Color.h
// Color.h has (#define)'s for different colors (add any color you desire).
//
// Add #include "CColorRadioButton.h" to your Dialogs Header file.
// Declare an instance of CColorRadioButton for each button being modified.
// Ex. CColorRadioButton m_radio;
//
// In your OnInitDialog() add a SubclassDlgItem for each CColorStatic member variable.
// Ex. m_radio.SubclassDlgItem(IDC_ST_TEXT, this);
// In this same function initialize your color for each piece of text unless you want the default.
// CColorRadioButton.cpp : implementation file
//
#include "stdafx.h"
#include "resource.h"
#include "ColorRadioButton.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CColorRadioButton
CColorRadioButton::CColorRadioButton()
{
m_crBkColor = ::GetSysColor(COLOR_3DFACE); // Initializing the Background Color to the system face color.
m_crTextColor = BLACK; // Initializing the text to Black
m_brBkgnd.CreateSolidBrush(m_crBkColor); // Create the Brush Color for the Background.
}
CColorRadioButton::~CColorRadioButton()
{
}
BEGIN_MESSAGE_MAP(CColorRadioButton, CStatic)
//{{AFX_MSG_MAP(CColorStatic)
ON_WM_CTLCOLOR_REFLECT()
ON_WM_CTLCOLOR()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CColorRadioButton message handlers
HBRUSH CColorRadioButton::CtlColor(CDC* pDC, UINT nCtlColor)
{
HBRUSH hbr;
hbr = (HBRUSH)m_brBkgnd; // Passing a Handle to the Brush
pDC->SetBkColor(m_crBkColor); // Setting the Color of the Text Background to the one passed by the Dialog
pDC->SetTextColor(m_crTextColor); // Setting the Text Color to the one Passed by the Dialog
if (nCtlColor) nCtlColor += 0;
return hbr;
}
void CColorRadioButton::SetBkColor(COLORREF crColor)
{
m_crBkColor = crColor; // Passing the value passed by the dialog to the member varaible for Backgound Color
m_brBkgnd.DeleteObject(); // Deleting any Previous Brush Colors if any existed.
m_brBkgnd.CreateSolidBrush(crColor); // Creating the Brush Color For the Static Text Background
RedrawWindow();
}
void CColorRadioButton::SetTextColor(COLORREF crColor)
{
m_crTextColor = crColor;
RedrawWindow();
}
void CColorRadioButton::SetCheck(UINT Value)
{
m_rb1Checked = Value;
RedrawWindow();
}
UINT CColorRadioButton::GetCheck()
{
return (m_rb1Checked);
}
void CColorRadioButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
UINT uStyle = Mystyle;
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CString strText;
UINT Option = DT_RIGHT;
CRect rcBullet(&lpDrawItemStruct->rcItem);
rcBullet.right = rcBullet.left + rcBullet.Height();
GetWindowText(strText);
pDC->FillSolidRect(&lpDrawItemStruct->rcItem,::GetSysColor((GetExStyle()&WS_EX_TRANSPARENT) ? COLOR_BTNFACE : COLOR_WINDOW));
UINT nState = DFCS_BUTTONRADIO|DFCS_ADJUSTRECT;
if (m_rb1Checked) nState |= DFCS_CHECKED;
pDC->DrawFrameControl(rcBullet, DFC_BUTTON, nState);
if ((uStyle & BS_RIGHT) == BS_RIGHT) Option = DT_RIGHT;
if ((uStyle & BS_LEFT) == BS_LEFT) Option = DT_LEFT;
if ((uStyle & BS_CENTER) == BS_CENTER) Option = DT_CENTER;
::SetTextColor(lpDrawItemStruct->hDC, m_crTextColor);
::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), &lpDrawItemStruct->rcItem, Option);
}
void CColorRadioButton::PreSubclassWindow()
{
CButton::PreSubclassWindow();
Mystyle =GetButtonStyle()| BS_OWNERDRAW;
SetButtonStyle(Mystyle);
}
in your's dlg header do
<<
CColorRadioButton m_radio_0;
>>
and then you can call it
<<`enter code here`
m_radio_0.SubclassDlgItem(IDC_RADIO1, this);
m_radio_0.SetTextColor(GREEN);
m_radio_0.SetCheck(BST_CHECKED);
>
It's Simply in fact
Isn't taking a screenshot on Windows thread-safe?
My following code sometimes takes some shots, but in most cases, the imgScreenshot (which is just a TImage) keeps being just plain white...
Am I missing something?
void __fastcall TCaptureThread::Execute()
{
int CurWidth = 1600;
int CurHeight = 900;
std::unique_ptr<TCanvas> Canvas(new TCanvas);
Canvas->Handle = GetDC(0);
FBMP = new TBitmap; // private class field
FBMP->Width = CurWidth;
FBMP->Height = CurHeight;
FR = Rect(0, 0, CurWidth, CurHeight); // private class field
while(!Terminated)
{
FBMP->Canvas->CopyRect(FR, Canvas, FR);
Synchronize(&UpdatePicture);
Sleep(100);
}
delete FBMP;
FBMP = NULL;
}
void __fastcall TCaptureThread::UpdatePicture()
{
FMainForm->imgScreenshot->Canvas->CopyRect(FR, FBMP->Canvas, FR);
}
Environment is C++ Builder 10.1.2 Berlin
Isn't taking a screenshot on Windows thread-safe?
Not when using VCL wrapper classes that are not thread-safe by default. If you were using plain Win32 API functions directly, then yes, it would be possible to write thread-safe code.
The main reason your code fails is because the VCL is designed to share GDI resources between multiple objects, and the main UI thread frequently frees unused/dormant GDI resources. So your worker thread's TBitmap image data is likely to get destroyed before you can call Synchronize() to copy it to your TImage.
That being said, what you are attempting can be done if you call Lock()/Unlock() on the Canvas objects in your worker thread, eg:
struct CanvasLocker
{
TCanvas *mCanvas;
CanvasLocker(TCanvas *C) : mCanvas(C) { mCanvas->Lock(); }
~CanvasLocker() { mCanvas->Unlock(); }
};
void __fastcall TCaptureThread::Execute()
{
int CurWidth = 1600;
int CurHeight = 900;
std::unique_ptr<TCanvas> Canvas(new TCanvas);
std::unique_ptr<TBitmap> BMP(new TBitmap);
FBMP = BMP.get();
{
CanvasLocker lock(Canvas); // <-- add this!
Canvas->Handle = GetDC(0);
}
{
CanvasLocker lock(BMP->Canvas); // <-- add this!
BMP->Width = CurWidth;
BMP->Height = CurHeight;
}
FR = Rect(0, 0, CurWidth, CurHeight);
while (!Terminated)
{
{
CanvasLocker lock1(Canvas); // <-- add this!
CanvasLocker lock2(BMP->Canvas); // <-- add this!
BMP->Canvas->CopyRect(FR, Canvas.get(), FR);
}
Synchronize(&UpdatePicture);
Sleep(100);
}
}
void __fastcall TCaptureThread::UpdatePicture()
{
CanvasLocker lock1(FBMP->Canvas); // <-- add this!
CanvasLocker lock2(FMainForm->imgScreenshot->Canvas); // <-- add this!
FMainForm->imgScreenshot->Canvas->CopyRect(FR, FBMP->Canvas, FR);
// or: FMainForm->imgScreenshot->Picture->Bitmap->Assign(FBMP);
}
That being said, because TCanvas is lockable, you might be able to get away with removing Synchronize() altogether:
void __fastcall TCaptureThread::Execute()
{
int CurWidth = 1600;
int CurHeight = 900;
std::unique_ptr<TCanvas> Canvas(new TCanvas);
std::unique_ptr<TBitmap> BMP(new TBitmap);
{
CanvasLocker lock(Canvas);
Canvas->Handle = GetDC(0);
}
{
CanvasLocker lock(BMP->Canvas);
BMP->Width = CurWidth;
BMP->Height = CurHeight;
}
TRect r = Rect(0, 0, CurWidth, CurHeight);
while (!Terminated)
{
{
CanvasLocker lock1(BMP->Canvas);
{
CanvasLocker lock2(Canvas);
BMP->Canvas->CopyRect(r, Canvas.get(), r);
}
CanvasLocker lock3(FMainForm->imgScreenshot->Canvas);
FMainForm->imgScreenshot->Canvas->CopyRect(r, BMP->Canvas, r);
// or: FMainForm->imgScreenshot->Picture->Bitmap->Assign(BMP);
}
Sleep(100);
}
}
I am trying to make a program currently that outputs a polygon to the desktop for a simple animation. The problem I am currently running into is that the animation gets an "onion" effect because the desktop isn't refreshing. I have searched for a method to refresh the desktop however because it's an animation, none of the solutions can refresh it fast enough. Below is an example of my code:
#include <iostream>
#include <Windows.h>
#include <math.h>
#include <Shlobj.h>
int main() {
//start ambrose
POINT amby[5];
POINT pos;
/* hide console window */
ShowWindow(FindWindowA("ConsoleWindowClass", NULL), false);
/* Calling GetDC with argument 0 retrieves the desktop's DC */
HDC hDC_Desktop = GetDC(0);
//This is just an example of what I am doing
for (int i = 0; i < 10000; i++) {
pos.x = 600+sin(double(i)/50)*200;
pos.y = 500+cos(double(i)/50)*200;
amby[0].x = -10+pos.x;
amby[0].y = -10+pos.y;
amby[1].x = -50+pos.x;
amby[1].y = -50+pos.y;
amby[2].x = 50+pos.x;
amby[2].y = -50+pos.y;
Polygon(hDC_Desktop,amby, 3);
Sleep(10);
}
//The method I was trying before that didn't work VVVVV
//LPITEMIDLIST pidl;
//SHGetSpecialFolderLocation(NULL,CSIDL_DESKTOP,&pidl);
//SHChangeNotify(SHCNE_ASSOCCHANGED,SHCNF_IDLIST,pidl,0);
return 0;
}
Thanks
Edit
I have tried using invalidateRect as such:
...
for (int i = 0; i < 10000; i++) {
pos.x = 600+sin(double(i)/50)*200;
pos.y = 500+cos(double(i)/50)*200;
amby[0].x = -10+pos.x;
amby[0].y = -10+pos.y;
amby[1].x = -50+pos.x;
amby[1].y = -50+pos.y;
amby[2].x = 50+pos.x;
amby[2].y = -50+pos.y;
Polygon(hDC_Desktop,amby, 3);
InvalidateRect(GetDesktopWindow(),NULL, true);
Sleep(10);
}
...
I am wondering if there is anyway to call WM_ERASEBKGND or WM_DISPLAYCHANGE to force a change. Does anyone know if there is a way to call these?
I am not sure what you are trying to achieve. Let me just answer to problem of onion effect. A quick and dirty solution to erase what was drawn in the previous iteration could be to draw using XOR mode but the solution has a few downsides, like flicker and color could be arbitrary. A proper solution that would address both the downsides would be to do all the drawing in a memory DC and BitBlt the same to the screen.
Code for the quick and dirty solution would be -
SetROP2(hDC_Desktop,R2_XORPEN);
//This is just an example of what I am doing
for (int i = 0; i < 100; i++)
{
if(i!=0)
{
pos.x = 600+sin(double(i-1)/50)*200;
pos.y = 500+cos(double(i-1)/50)*200;
amby[0].x = -10+pos.x;
amby[0].y = -10+pos.y;
amby[1].x = -50+pos.x;
amby[1].y = -50+pos.y;
amby[2].x = 50+pos.x;
amby[2].y = -50+pos.y;
Polygon(hDC_Desktop,amby, 3);
}
pos.x = 600+sin(double(i)/50)*200;
pos.y = 500+cos(double(i)/50)*200;
amby[0].x = -10+pos.x;
amby[0].y = -10+pos.y;
amby[1].x = -50+pos.x;
amby[1].y = -50+pos.y;
amby[2].x = 50+pos.x;
amby[2].y = -50+pos.y;
Polygon(hDC_Desktop,amby, 3);
Sleep(10);
}
There's an easy solution, and that's to not actually draw on the desktop. Instead, create a transparent full-screen window. Since it's transparent, any pixel that you don't draw will show the desktop underneath. Hence, only your polygon pixels will hide the underlying desktop.
As a result, the desktop window never needs to be invalidated or repainted etc.
Why don't you use a transparent wnd.
class COverlayWnd : public CWnd
{
DECLARE_DYNAMIC(COverlayWnd)
public:
COverlayWnd();
virtual ~COverlayWnd();
protected:
afx_msg void OnPaint();
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
// OverlayWnd.cpp : implementation file
//
The implementation. Just move the window if you want animations to run all over the desktop.
#include "stdafx.h"
// COverlayWnd
IMPLEMENT_DYNAMIC(COverlayWnd, CWnd)
COverlayWnd::COverlayWnd()
{
}
COverlayWnd::~COverlayWnd()
{
}
BEGIN_MESSAGE_MAP(COverlayWnd, CWnd)
ON_WM_PAINT()
ON_WM_CREATE()
END_MESSAGE_MAP()
void COverlayWnd::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect( &rect );
dc.FillSolidRect(&rect, RGB(1,1,1));
//paint other stuff that don't have RGB(1,1,1)
}
int COverlayWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
BOOL bRet = 0;
bRet = ModifyStyleEx(0,WS_EX_LAYERED|WS_EX_TRANSPARENT);
bRet = ModifyStyle(DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU,0);
bRet = ModifyStyle(WS_POPUP,0);
bRet = SetLayeredWindowAttributes(RGB(1,1,1),0,LWA_COLORKEY);
//the RGB(1,1,1) is the transparent color
ASSERT(bRet);
//this->EnableWindow(FALSE);
return 0;
}
I'm using a CListCtrl/CListView report view (LVS_REPORT) in virtual mode (LVS_OWNERDATA) with LVS_EX_DOUBLEBUFFER enabled and I encounter ugly flickering. Double buffer have a real effect but it doesn't stop all flickering (without it very slow).
I'm not looking for switching to other controls that would require a high amount of rework (like ObjectListView)
How does the flickering behaves:
on column resize - the background is first clean using lightgray and after this is displayed the text (background is white)
on mouse scroll (animated) - for a very short time there is lightgray-bar displayed in the area where new lines are to be displayed.
It looks like it does clean the background using the default window background color (lightgray) for the area where it has to redraw.
How do I solve the flickering problem?
Try to do the following:
- Set Clip Children and Clip Sibling for paremt dialog of List Control.
- Make dirived from CListCtrl class. In this class overwrite OnEraseBkgnd. In the OnEraseBkgnd fill with background color area around of visible items of the list.
The OnEraseBkgnd can look like:
BOOL CListCtrlEx::OnEraseBkgnd(CDC* pDC)
{
CBrush br;
CRect rcCli;
CRect rcItemsRect(0, 0, 0, 0);
int nHeadHeight = 0;
int nItems = GetItemCount();
GetClientRect(&rcCli);
CHeaderCtrl* pHeadCtrl = GetHeaderCtrl();
if (pHeadCtrl)
{
CRect rcHead;
pHeadCtrl->GetWindowRect(&rcHead);
nHeadHeight = rcHead.Height();
}
rcCli.top += nHeadHeight;
if (nItems > 0)
{
CPoint ptItem;
CRect rcItem;
GetItemRect(nItems - 1, &rcItem, LVIR_BOUNDS);
GetItemPosition(nItems - 1, &ptItem);
rcItemsRect.top = rcCli.top;
rcItemsRect.left = ptItem.x;
rcItemsRect.right = rcItem.right;
rcItemsRect.bottom = rcItem.bottom;
if (GetExtendedStyle() & LVS_EX_CHECKBOXES)
rcItemsRect.left -= GetSystemMetrics(SM_CXEDGE) + 16;
}
br.CreateSolidBrush(GetBkColor());
if (rcItemsRect.IsRectEmpty())
pDC->FillRect(rcCli, &br);
else
{
if (rcItemsRect.left > rcCli.left) // fill left rectangle
pDC->FillRect(
CRect(0, rcCli.top, rcItemsRect.left, rcCli.bottom), &br);
if (rcItemsRect.bottom < rcCli.bottom) // fill bottom rectangle
pDC->FillRect(
CRect(0, rcItemsRect.bottom, rcCli.right, rcCli.bottom), &br);
if (rcItemsRect.right < rcCli.right) // fill right rectangle
pDC->FillRect(
CRect(rcItemsRect.right, rcCli.top, rcCli.right, rcCli.bottom), &br);
}
return TRUE;
}
I know only way to have flicker free is using double buffering or MemDC.
have found this article: Flicker-free-drawing-of-any-control
This article explains it well how to quickly perform Non Flickering drawing on your CListCtrl.
And it works excellent.
PS: VS 2005 doesn't have CMemDC class you will need to implement it your self, or use the following code:
//
// CMemDC.h header file
//
#pragma once
class CMemDC
{
public:
CMemDC(CDC& dc, CWnd* pWnd);
CMemDC(CDC& dc, const CRect& rect);
virtual ~CMemDC();
CDC& GetDC() { return m_bMemDC ? m_dcMem : m_dc; }
BOOL IsMemDC() const { return m_bMemDC; }
BOOL IsVistaDC() const { return m_hBufferedPaint != NULL; }
void EraseBkClip();
protected:
CDC& m_dc;
BOOL m_bMemDC;
HANDLE m_hBufferedPaint;
CDC m_dcMem;
CBitmap m_bmp;
CBitmap* m_pOldBmp;
CRect m_rect;
};
//
// CMemDC.cpp source file
//
#include "CMemDC.h"
CMemDC::CMemDC(CDC& dc, CWnd* pWnd) :
m_dc(dc), m_bMemDC(FALSE), m_hBufferedPaint(NULL), m_pOldBmp(NULL)
{
ASSERT_VALID(pWnd);
pWnd->GetClientRect(m_rect);
m_rect.right += pWnd->GetScrollPos(SB_HORZ);
m_rect.bottom += pWnd->GetScrollPos(SB_VERT);
if (m_dcMem.CreateCompatibleDC(&m_dc) &&
m_bmp.CreateCompatibleBitmap(&m_dc, m_rect.Width(), m_rect.Height()))
{
m_bMemDC = TRUE;
m_pOldBmp = m_dcMem.SelectObject(&m_bmp);
}
}
CMemDC::CMemDC(CDC& dc, const CRect& rect) :
m_dc(dc), m_bMemDC(FALSE), m_hBufferedPaint(NULL), m_pOldBmp(NULL), m_rect(rect)
{
ASSERT(!m_rect.IsRectEmpty());
if (m_dcMem.CreateCompatibleDC(&m_dc) &&
m_bmp.CreateCompatibleBitmap(&m_dc, m_rect.Width(), m_rect.Height()))
{
m_bMemDC = TRUE;
m_pOldBmp = m_dcMem.SelectObject(&m_bmp);
}
}
CMemDC::~CMemDC()
{
if (m_bMemDC)
{
CRect rectClip;
int nClipType = m_dc.GetClipBox(rectClip);
if (nClipType != NULLREGION)
{
if (nClipType != SIMPLEREGION)
{
rectClip = m_rect;
}
m_dc.BitBlt(rectClip.left, rectClip.top, rectClip.Width(), rectClip.Height(), &m_dcMem, rectClip.left, rectClip.top, SRCCOPY);
}
m_dcMem.SelectObject(m_pOldBmp);
}
}
void CMemDC::EraseBkClip()
{
CRect clip;
m_dcMem.GetClipBox(&clip);
m_dcMem.FillSolidRect(clip, GetSysColor(COLOR_WINDOW));
}
There is an ultra simple way I found that worked for me:
Turn off redraw with m_List1.SetRedraw(false)
Reset contents with m_List1.ResetContents()
Add new strings in loop with m_List1.AddString()
Then finalize by turning back on redraw and a m_List1.UpdateWindow().