begin process at 2012 05 27 13:48:55
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

API

 > BASE POUR L'UTILISATION DU GDI (API WINDOWS)

BASE POUR L'UTILISATION DU GDI (API WINDOWS)


 Information sur la source

Note :
9 / 10 - par 1 personne
9,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :API Classé sous :api windows, gdi, graphique, double buffering, win32 Niveau :Débutant Date de création :29/04/2008 Date de mise à jour :19/02/2009 22:04:48 Vu / téléchargé :7 053 / 174

Auteur : _michel

Ecrire un message privé
Commentaire sur cette source (34)
Ajouter un commentaire et/ou une note

 Description

J'ai eu assez de mal à savoir commment utiliser le GDI (la solution n'est pas donnée toute faite dans msdn) et donc je voulais faire partager ma très modeste experience.
Comme ça quelqu'un au bord de la crise de nerf pourra toujours copier-coller ce code sans se farcir toutes les docs pour déterminer comment faire marcher ce sacré bazard (API + GDI).

Le GDI, de mon point de vue, est assez compliqué, mais permet de faire tout ce qu'on veux faire pour des application simples, et ne necessite pas d'ajouter des bibliothèques graphiques comme la SDL, OpenGL (je veux parler de GLUT et WGL), GTK, Qt... qui encapsulent carrement l'API Windows. Ainsi le GDI s'integre parfaitement dans une programme basé sur l'API Windows (normal puisqu'il fait partie de l'API Windows), et ne la limite pas dans l'utilisation des controls (menus, boutons, dialog boxes...).

Cette source est juste un départ qui permet d'utiliser un pseudo "double buffering", une technique qui permet de ne pas afficher l'écran pendant la création du dessin. Même dans des application simple, on est rapidement obliger de faire comme ça, sinon c'est vraiment trop moche. Je dis "pseudo" car même si au final ça donne le même effet, on ne procède pas pareil que si on veut faire du vrai "double buffering" : normalement il faut avoir deux buffers video, un qui est affiché ("front buffer") et un dans lequel on compose l'image ("back buffer"), et les permuter quand on veux redessiner l'écran (le "front buffer" devient "back buffer" et vis-versa). Il suffit de permuter deux adresse mémoires, ce qui est extremement rapide.
Avec le GDI, on a pas acces à la mémoire video (ou alors je veux absolument savoir comment), donc il faut ruser. On crée un "memory device context", c'est à dire un espace mémoire qui fait office de "back buffer", et quand on veut réafficher il faut le copier dans la mémoire vidéo affichable. Mais au final, me direz-vous, c'est la même chose : une copie. Mais non, car là il faut copier octet par octet toute la mémoire, et pas seulement changer deux pointeurs. On le fait avce BitBlt(), une fonction du GDI.

Avec cette source, on a également la possiblité d'acceder directement à la mémoire du bitmap et donc de créer ses propres fonctions graphiques sans passer par le très très long SetPixel() ou même SetPixelV(). Pour cela, on utilise CreateDIBSection() au lieu de CreateCompatibleBitmap() lors de la création du bitmap. Encore faut-il que le device (écran) sur lequel on affiche supporte le 24bpp, mais normalement il y a pas de problème...

En résumé, dans ce code de base il y a les deux techniques élementaires qui permettent d'utiliser facilement le GDI.

Evidement, on a pas toutes les performances qu'on peut espérer avec des librairies graphiques comme OpenGL ou DirectX, mais pour autre chose qu'une application graphique, c'est pour moi la meilleur solution.

Source

  • #include <windows.h>
  • #include <time.h>
  • #include <stdlib.h>
  • #define LARGEUR 600
  • #define HAUTEUR 400
  • HBITMAP hBmp;
  • BITMAP Bmp;
  • HDC hMDC; // Memory Device Context
  • HINSTANCE hInstance;
  • LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  • // Note : ne pas oublier de supprimer l'objet avec DeleteObject() quand on n'en a plus besoin.
  • long BitmapCreation (unsigned long taille_x, unsigned long taille_y, HBITMAP *hBitmap, BITMAP *Bitmap);
  • int WINAPI WinMain(HINSTANCE hThisHinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
  • {
  • HWND hwnd;
  • MSG msg;
  • WNDCLASS wc;
  • RECT Client;
  • UNREFERENCED_PARAMETER (hPrevInstance);
  • UNREFERENCED_PARAMETER (lpCmdLine);
  • hInstance = hThisHinstance;
  • wc.style = 0;
  • wc.lpfnWndProc = WndProc;
  • wc.cbClsExtra = 0;
  • wc.cbWndExtra = 0;
  • wc.hInstance = hInstance;
  • wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  • wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  • wc.hbrBackground = (HBRUSH)(1 + COLOR_BTNFACE);
  • wc.lpszMenuName = NULL;
  • wc.lpszClassName = "Classe principale";
  • if(!RegisterClass(&wc)) return FALSE;
  • Client.left = 0;
  • Client.top = 0;
  • Client.right = LARGEUR;
  • Client.bottom = HAUTEUR;
  • AdjustWindowRectEx (&Client, WS_OVERLAPPEDWINDOW & (~(WS_THICKFRAME | WS_MAXIMIZEBOX)), FALSE, 0);
  • if ((hwnd = CreateWindowEx (
  • 0,
  • "Classe principale",
  • "Test de l'utilisation de memory DC pour le \"double buffering\".",
  • WS_OVERLAPPEDWINDOW & (~(WS_THICKFRAME | WS_MAXIMIZEBOX)),
  • CW_USEDEFAULT, CW_USEDEFAULT,
  • Client.right - Client.left, Client.bottom - Client.top,
  • NULL,
  • NULL,
  • hInstance,
  • NULL)
  • ) == NULL
  • ){
  • return FALSE;
  • }
  • ShowWindow(hwnd, nCmdShow);
  • UpdateWindow(hwnd);
  • while (GetMessage(&msg, NULL, 0, 0))
  • {
  • TranslateMessage(&msg);
  • DispatchMessage(&msg);
  • }
  • return msg.wParam;
  • }
  • LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  • {
  • HDC hDC; // Display Device Context
  • PAINTSTRUCT ps;
  • HBRUSH hBrush, hOldBrush;
  • switch (uMsg)
  • {
  • case WM_CREATE:
  • // Determine le handle du DC
  • if ((hDC = GetDC (hwnd)) == NULL) {
  • return -1;}
  • // Crée un memory DC
  • if ((hMDC = CreateCompatibleDC (hDC)) == NULL) {
  • return -1;}
  • // Crée un bitmap
  • if (BitmapCreation (LARGEUR, HAUTEUR, &hBmp, &Bmp) == EXIT_FAILURE) {
  • return -1;}
  • // Selectionne ce bitmap dans hMDC
  • if (SelectObject (hMDC, hBmp) == NULL) {
  • return -1;}
  • ReleaseDC (hwnd, hDC);
  • // Initialise l'affichage
  • Ellipse (hMDC, 0, 0, LARGEUR, HAUTEUR);
  • // Initialise le générateur de nombres aléatoires
  • srand ((unsigned int)time (NULL));
  • return 0;
  • case WM_DESTROY:
  • DeleteDC (hMDC);
  • DeleteObject (hBmp);
  • PostQuitMessage(0);
  • return 0;
  • case WM_KEYDOWN:
  • // Nouvel affichage dans hMDC
  • hBrush = CreateSolidBrush (RGB (rand()%256, rand()%256, rand ()%256));
  • hOldBrush = SelectObject (hMDC, hBrush);
  • Ellipse (hMDC, 0, 0, LARGEUR, HAUTEUR);
  • SelectObject (hMDC, hOldBrush);
  • DeleteObject (hBrush);
  • // Raffraichit l'écran
  • RedrawWindow (hwnd, NULL, NULL, RDW_INVALIDATE);
  • return 0;
  • case WM_PAINT:
  • // Copie hMDC dans hDC
  • hDC = BeginPaint (hwnd, &ps);
  • BitBlt (hDC, 0, 0, LARGEUR, HAUTEUR, hMDC, 0, 0, SRCCOPY);
  • EndPaint (hwnd, &ps);
  • return 0;
  • default:
  • return DefWindowProc(hwnd, uMsg, wParam, lParam);
  • }
  • }
  • long BitmapCreation (unsigned long taille_x, unsigned long taille_y, HBITMAP *hBitmap, BITMAP *Bitmap)
  • {
  • BITMAPINFO BitmapInfo;
  • void *tmp;
  • // Crée le bitmap
  • BitmapInfo.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
  • BitmapInfo.bmiHeader.biWidth = taille_x;
  • BitmapInfo.bmiHeader.biHeight = taille_y;
  • BitmapInfo.bmiHeader.biPlanes = 1;
  • BitmapInfo.bmiHeader.biBitCount = 32;
  • BitmapInfo.bmiHeader.biCompression = BI_RGB;
  • BitmapInfo.bmiHeader.biSizeImage = taille_x * taille_y * 4;
  • BitmapInfo.bmiHeader.biXPelsPerMeter = 0;
  • BitmapInfo.bmiHeader.biYPelsPerMeter = 0;
  • BitmapInfo.bmiHeader.biClrUsed = 0;
  • BitmapInfo.bmiHeader.biClrImportant = 0;
  • if ((*hBitmap = CreateDIBSection (NULL, &BitmapInfo, DIB_RGB_COLORS, &tmp, NULL, 0)) == NULL) {
  • return EXIT_FAILURE;
  • }
  • // Obtient la structure BITMAP
  • if (GetObject (*hBitmap, sizeof (BITMAP), Bitmap) != sizeof (BITMAP)){
  • DeleteObject (*hBitmap);
  • return EXIT_FAILURE;
  • }
  • return EXIT_SUCCESS;
  • }
#include <windows.h>
#include <time.h>
#include <stdlib.h>


#define LARGEUR 600
#define HAUTEUR 400

HBITMAP hBmp;
BITMAP Bmp;
HDC hMDC;					// Memory Device Context

HINSTANCE hInstance;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// Note : ne pas oublier de supprimer l'objet avec DeleteObject() quand on n'en a plus besoin.
long BitmapCreation (unsigned long taille_x, unsigned long taille_y, HBITMAP *hBitmap, BITMAP *Bitmap);


int WINAPI WinMain(HINSTANCE hThisHinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	HWND hwnd;
	MSG msg;
	WNDCLASS wc;
	RECT Client;

	UNREFERENCED_PARAMETER (hPrevInstance);
	UNREFERENCED_PARAMETER (lpCmdLine);

	hInstance = hThisHinstance;

	wc.style = 0;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(1 + COLOR_BTNFACE);
	wc.lpszMenuName =  NULL;
	wc.lpszClassName = "Classe principale";

	if(!RegisterClass(&wc)) return FALSE;

	Client.left = 0;
	Client.top = 0;
	Client.right = LARGEUR;
	Client.bottom = HAUTEUR;
	AdjustWindowRectEx (&Client, WS_OVERLAPPEDWINDOW & (~(WS_THICKFRAME | WS_MAXIMIZEBOX)), FALSE, 0);

	if ((hwnd = CreateWindowEx (
			0,
			"Classe principale",
			"Test de l'utilisation de memory DC pour le \"double buffering\".",
			WS_OVERLAPPEDWINDOW & (~(WS_THICKFRAME | WS_MAXIMIZEBOX)),
			CW_USEDEFAULT, CW_USEDEFAULT,
			Client.right - Client.left, Client.bottom - Client.top,
			NULL, 
			NULL, 
			hInstance, 
			NULL)
		) == NULL
	){
		return FALSE;
	}

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HDC hDC;			// Display Device Context
	PAINTSTRUCT ps;
	HBRUSH hBrush, hOldBrush;

	switch (uMsg)
	{
	case WM_CREATE:
		// Determine le handle du DC
		if ((hDC = GetDC (hwnd)) == NULL) {
			return -1;}

		// Crée un memory DC
		if ((hMDC = CreateCompatibleDC (hDC)) == NULL) {
			return -1;}

		// Crée un bitmap
		if (BitmapCreation (LARGEUR, HAUTEUR, &hBmp, &Bmp) == EXIT_FAILURE) {
			return -1;}

		// Selectionne ce bitmap dans hMDC
		if (SelectObject (hMDC, hBmp) == NULL) {
			return -1;}

		ReleaseDC (hwnd, hDC);

		// Initialise l'affichage
		Ellipse (hMDC, 0, 0, LARGEUR, HAUTEUR);

		// Initialise le générateur de nombres aléatoires
		srand ((unsigned int)time (NULL));

		return 0;

	case WM_DESTROY:
		DeleteDC (hMDC);
		DeleteObject (hBmp);
		PostQuitMessage(0);
		return 0;

	case WM_KEYDOWN:
		// Nouvel affichage dans hMDC
		hBrush = CreateSolidBrush (RGB (rand()%256, rand()%256, rand ()%256));
		hOldBrush = SelectObject (hMDC, hBrush);
		Ellipse (hMDC, 0, 0, LARGEUR, HAUTEUR);
		SelectObject (hMDC, hOldBrush);
		DeleteObject (hBrush);

		// Raffraichit l'écran
		RedrawWindow (hwnd, NULL, NULL, RDW_INVALIDATE);
		return 0;

	case WM_PAINT:
		// Copie hMDC dans hDC
		hDC = BeginPaint (hwnd, &ps);
		BitBlt (hDC, 0, 0, LARGEUR, HAUTEUR, hMDC, 0, 0, SRCCOPY);
		EndPaint (hwnd, &ps);
		return 0;

	default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
}


long BitmapCreation (unsigned long taille_x, unsigned long taille_y, HBITMAP *hBitmap, BITMAP *Bitmap)
{
	BITMAPINFO BitmapInfo;
	void *tmp;

	// Crée le bitmap
	BitmapInfo.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
	BitmapInfo.bmiHeader.biWidth = taille_x;
	BitmapInfo.bmiHeader.biHeight = taille_y;
	BitmapInfo.bmiHeader.biPlanes = 1;
	BitmapInfo.bmiHeader.biBitCount = 32;
	BitmapInfo.bmiHeader.biCompression = BI_RGB;
	BitmapInfo.bmiHeader.biSizeImage = taille_x * taille_y * 4;
	BitmapInfo.bmiHeader.biXPelsPerMeter = 0;
	BitmapInfo.bmiHeader.biYPelsPerMeter = 0;
	BitmapInfo.bmiHeader.biClrUsed = 0;
	BitmapInfo.bmiHeader.biClrImportant = 0;
	if ((*hBitmap = CreateDIBSection (NULL, &BitmapInfo, DIB_RGB_COLORS, &tmp, NULL, 0)) == NULL) {
		return EXIT_FAILURE;
	}

	// Obtient la structure BITMAP
	if (GetObject (*hBitmap, sizeof (BITMAP), Bitmap) != sizeof (BITMAP)){
		DeleteObject (*hBitmap);
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

 Conclusion

Vous aurez sans doute remarqué, si vous avez testé cette source, qu'on peut constater un effet assez désagréable : de temps en temps on peut apercevoir un trait horizontal lors du réaffichage de l'écran.
Selon moi c'est un problème de syncronisation verticale, comme dans les jeux vidéos : BitBlt() n'a pas fini de copier hMDC dans hDC quand l'écran affiche physiquement l'image. (J'ai un écran LCD réglé à 60Hz.)
Dites moi si on peut régler ça.

 Fichier Zip

Les Membres Club peuvent télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !
  •   BaseGDI
    •   BaseGDI
      • BaseGDI.vcprojTélécharger ce fichier [Réservé aux membres club]3 938 octets
      • main.cTélécharger ce fichier [Réservé aux membres club]Voir ce fichier4 310 octets
    • Release
    • BaseGDI.slnTélécharger ce fichier [Réservé aux membres club]Voir ce fichier887 octets
    • BaseGDI.suoTélécharger ce fichier [Réservé aux membres club]8 192 octets

Télécharger le zip


 Historique

30 avril 2008 00:54:08 :
Correction de choses peu rigoureuses.
30 avril 2008 18:22:22 :
Modification du code pour pouvoir accéder à la mémoire du bitmap.
30 avril 2008 18:26:18 :
Modification du code pour pouvoir accéder à la mémoire du bitmap.
19 février 2009 22:04:49 :
Ajout de l'executable à la demande du public.

 Sources du même auteur

PROXY IRC SIMPLE (WINDOWS/WINSOCK)

 Sources de la même categorie

Source avec Zip WIN32 TLS LENT par dguilmain
Source avec Zip VIDER ELEMENTS DE CORBEILLE WINDOWS7 (WIN64) par BruNews
Source avec Zip Source avec une capture FIND TEXT (WIN64) par BruNews
Source avec Zip DELETE DIRECTORY (WIN64) par BruNews
Source avec Zip ENUM DIRECTORY (WIN64) par BruNews

 Sources en rapport avec celle ci

Source avec Zip Source avec une capture CLASSE GRAPH: GESTION DES GRAPHIQUES DANS LES APPLICATIONS W... par Pistol_Pete
Source avec Zip Source avec une capture ONGLETS MULTICOLORES (WIN32 API) par racpp
Source avec Zip UN SIMPLE EDITEUR DE TEXTE EN C AVEC L'API WIN32 par flofloo
Source avec Zip Source avec une capture CALCULER SES MOYENNES par uaip
Source avec Zip Source avec une capture VERSION MODIFIÉE DE "JEU DE PIONS CONTRE LE PC" par Foragan

Commentaires et avis

Commentaire de BruNews le 30/04/2008 00:35:51 administrateur CS

Enlève exit(), ça n'a rien à faire dans du Windows GUI, remplace par un branchement vers PostQuitMessage(0) de WM_DESTOY, un petit goto judicieux ira parfaitement.

Referme hDC obtenu dans WM_CREATE et utilise le retour de BeginPaint pour dessiner dans WM_PAINT.

Commentaire de _michel le 30/04/2008 00:58:21

exit() -> dans le traitement de WM_CREATE, la solution est de retourner -1 d'après la doc

pour hDC je l'ai mis en Common Display Device Context plutot que en Private Display Device Context, vu que seul hMDC a besoin d'être accedé courament. Pas de problème constaté.

Commentaire de Renfield le 30/04/2008 07:31:22 administrateur CS

doit être possible de jouer juste avec SelectObject pour permutter front et backbuffer

en switchant les hBmp

Commentaire de _michel le 30/04/2008 12:02:39

Renfield -> Non je ne crois pas : selectionner un bitmap dans un DC ne le fait pas s'afficher. Il n'y a qu'en passant dpar un memory DC que ça marche.

Commentaire de _michel le 30/04/2008 13:43:02

En fait, de manière générale, on ne peut pas selectionner un bmp dans autre chose qu'un memory DC (cf doc de SelectObject).

Commentaire de _michel le 30/04/2008 18:28:02

J'ai modifié le code pour pouvoir modifier le bitmap par soit même (pas seulement avec les fonctions du GDI).

Commentaire de f_l_a_s_h_b_a_c_k le 30/04/2008 18:54:17

moi je remplacerais

  while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}  

par


while( 1 )     {
if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
{
    if( !GetMessage( &msg, NULL, 0, 0 ) ) return msg.wParam;
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}
        else
{

/////////////////
     un flip();
     ou une fonction sur ton BitBlt()
//////////////////
}


ou lieu de le mettre ton bitblt() dans WM_PAINT:


sa devrais eliminer ton 'flicker'

Commentaire de _michel le 30/04/2008 21:53:21

J'ai essayé comme ça :




for(;;) {
if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
{
if( !GetMessage( &msg, NULL, 0, 0 ) ) return msg.wParam;
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
HDC hDC = GetDC (hwnd);
BitBlt (hDC, 0, 0, LARGEUR, HAUTEUR, hMDC, 0, 0, SRCCOPY);
ReleaseDC (hwnd, hDC);
}
}



Ca ne change pas mon problème : il y a toujours un manque de syncronisation entre le raffraichissement de l'écran et le raffraichissement de la mémoire.
Le seul changement, c'est que l'image est raffraichie le plus souvent possible, ce qui prend tout le temps processeur. Bien entendu c'est l'optimal pour un jeu en full-screen, mais pour une application qui utilise le GDI et qui n'est pas complemtement axée sur le grapisme, c'est pas ce qu'il faut.
Je pense qu'on peut obtenir le même résultat en n'appelant pas EndPaint() pendant le traitement d'un message WM_PAINT : la fenêtre a toujours une zone invalide et donc Windows envoie sans arret des messages WM_PAINT.

Commentaire de f_l_a_s_h_b_a_c_k le 01/05/2008 23:07:37

pas sure de getDC()
je ferais plus ca...


void dess(HWND hwnd)
{
PAINTSTRUCT  PaintStruct ;



  HDC DC = BeginPaint(hwnd, &PaintStruct);
  HDC hMDC = CreateCompatibleDC(DC);


HBITMAP OldBmp = SelectBitmap(hMDC, le_bitmap);

BitBlt (hDC, 0, 0, LARGEUR, HAUTEUR, hMDC, 0, 0, SRCCOPY);

DeleteDC(PicDC);
EndPaint(hwnd, &PaintStruct);

}


for(;;) {
if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
{
if( !GetMessage( &msg, NULL, 0, 0 ) ) return msg.wParam;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else { Dess(hwnd); }
}

Commentaire de _michel le 02/05/2008 11:52:37

Tu as testé chez toi?
J'ai essayé chez moi, en modifiant un peu :



void Dess(HWND hwnd)
{
PAINTSTRUCT  PaintStruct ;
HDC DC;

DC = BeginPaint(hwnd, &PaintStruct);
BitBlt (DC, 0, 0, LARGEUR, HAUTEUR, hMDC, 0, 0, SRCCOPY);
EndPaint(hwnd, &PaintStruct);
}

Ca ne marche pas (si en plus on enlève le traitement des WP_PAINT, la fenêtre n'affiche rien).

Je crois pas qu'on puisse dessiner avec BeginPaint()/EndPaint() en dehors d'un WM_PAINT : la zone invalide est vide, donc tous les opérations graphiques qu'on effectue ne servent à rien. En tous cas msdn spécifie qu'il faut utiliser GetDc()/ReleaseDC() en dehors d'un WP_PAINT.

Commentaire de shuttleur le 27/06/2008 09:58:04

Salut _michel

Merci de faire partager le résultat de tes recherches.
Je suis confronté à des problèmes de performances également sur une appli totalement skinnée avec des bitmaps.

Je cherche à savoir quelle est la meilleure combinaison de paramètres dwExStyle et dwStyle dans CreateWindowEx pour les performances.

Par exemple, je ne veut pas de contour ni de barre de titre windows, est ce que je dois demander une fenetre sans partie non cliente, et peindre une fausse barre de titre dans la partie cliente ou demander une barre de titre et des contours, mais surcharger le message PAINT de la partie non cliente pour la faire à mon gout ?

J'ai remarqué assez vite des problèmes de performances, et des "pertes de messages windows", qui peuvent être résolues en mettant des DoEvents (la boucle infinie qui traite la pile des messages, la boucle même dont vous parlez avec F_L_A_S_H_B_A_C_K), je suis étonné de devoir en arriver là, ca ne paraît pas très propre..

Est tu sur que SetPixel est lent ?
Comment le fait d'utiliser CreateDIBSection() et de tout faire à la main peut il être plus rapide que d'utiliser les fonctions windows faites pour ?

Merci d'avance

Commentaire de Renfield le 27/06/2008 10:03:15 administrateur CS

tu peux modifier la taille et disposition de la partie non cliente,
j'ai deja fait ca, et en VB, sans grosse pertes de perfs..

SetPixel est lent: dessine ce pixel en rouge, celui la aussi, celui là, encore.
mais entretemps, surtout raffraichit, hein !

CreateDIBSection va permettre de faire un image stockée en RAM (Device Independant, donc pas d'echange constant avec la carte graphique)
et on affiche le tout en une fois.
clairement plus rapide, donc.

Commentaire de shuttleur le 27/06/2008 10:14:14

Pourquoi rafraichirait-il entre deux setpixels consécutifs encapsulés entre un beginpaint et un endpaint ?

Ne rafraichit-il pas uniquement au endpaint ?

Commentaire de _michel le 28/06/2008 11:37:57

Attendez quelques instants, je me remet le nez dans ces trucs là...

Commentaire de _michel le 28/06/2008 12:32:24

En fait, BeginPaint/EndPaint ne sert pas à raffraichir, ça sert juste à donner des informations de DC à ta fenêtre (PAINTSTRUCT).
C'est d'ailleur un gros problème quand on utilise pas le double buffering (n'ai-je pas déjà évoqué ça?) : si on peint toute la surface de la fenêtre d'une couleur unie et qu'ensuite on dessine un autre contenu (entre BeginPaint/EndPaint), on va voir un scintillement désagréable : le flickering.

Pour ce qui est de SetPixel (ou SetPixelV qui est un peu plus rapide), ça doit être environ 10000 fois moins rapide qu'un accès mémoire (je ne plaisante pas).
Voila pourquoi on peut utiliser les fonctions GDI (par exemple pour faire des ellipses), mais SetPixel le moins possible.

Tu demande aussi si il est plus rapide de faire soit-même sa partie non cliente. Je pense que non, mais tu as toujours le choix de ne pas en avoir du tout (donc pas de style WS_OVERLAPPEDWINDOW), ça donne des programmes un peu dépouillés, mais il y en a qui aiment (pas moi). En tout cas si tu veux faire toi-même ta partie non cliente, je te souhaite bien du courage, pour gérer le redimentionnement et le déplacement par exemple. En plus ton programme final ne pourra pas resembler aux fenêtres standard Windows dans tous les Windows (les fenêtres XP ne ressemblent pas aux fenêtre 98 par exemple).

"J'ai remarqué assez vite des problèmes de performances, et des "pertes de messages windows", qui peuvent être résolues en mettant des DoEvents (la boucle infinie qui traite la pile des messages, la boucle même dont vous parlez avec F_L_A_S_H_B_A_C_K), je suis étonné de devoir en arriver là, ca ne paraît pas très propre.."
En effet ça ne paraît pas très propre... Déjà ce DoEvents, c'est du C ou du VB (My.Application.DoEvents Method) ? Parce que je connaît pas en C et ma doc non plus.
Normalement si il y a des pertes de messages, ça doit venir de toi. Envoie ta boucle de message qui ne fonctionne pas.

Commentaire de shuttleur le 30/06/2008 10:02:12

ok, je comprends, je m'étais mis en tête que le flickering n'était pas possible entre les beginpaint() et endpaint() pcq le DC n'était rafraichit qu'au moment du endpaint().
Je comprends donc mieux maintenant l'intérêt du double buffering, même si dans certains cas il semble ralentir le temps de traitement du WM_PAINT.

Voici ma boucle de messages :

MSG Msg;
while( GetMessage(&Msg, 0, 0, 0))
{
TranslateMessage( &Msg );
DispatchMessage( &Msg );
}

A certains endroits de mon application, si j'insiste un peu trop avec des SetWindowPos et des MoveWindow, c'est flagrant, Windows gère mal les messages et position/dimensionne la fenêtre n'importe comment.
En ajoutant un Sleep(), ca ne change rien.
Si j'enlève qques SetWindowPos, ca marche bien.

Il a donc fallu que j'utilise l'équivalent des DoEvents(), ca permet de faire suivre l'affichage, ca prend jusqu'à 50ms par contre. Voici le code que j'utilise :
    MSG msg;
    while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        if(msg.message == WM_QUIT)
break;

        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

Je suis étonné de devoir utiliser ce genre d'astuces à certains endroits de mon application, sans ca tous les messages ne sont pas traités correctement.

Commentaire de shuttleur le 30/06/2008 10:05:26

"En tout cas si tu veux faire toi-même ta partie non cliente, je te souhaite bien du courage, pour gérer le redimentionnement et le déplacement par exemple."

j'utilise cette technique pour redimensionner et déplacer ma fenetre. (j'ai dessiné dans le coin en bas à droite un grip pour le resize)

case WM_NCHITTEST:
    if((rect.bottom-cursor.y)<20 && (rect.right-cursor.x<20))
return HTBOTTOMRIGHT;
    else
return HTCAPTION;

Ca marche nikel, et sans partie non cliente visible

Commentaire de _michel le 30/06/2008 10:40:36

Hahahahaha!!! UN FOU!!! Pas mal le traitement du WM_NCHITEST, je connaissait même pas (DefWindowProc fait le boulot pour moi).
Par contre, c'est pas normal pour les PeekMessage, la boucle que tu donne là finit dès qu'il n'y a plus de messages dans la thread message queue. Donc si je comprend bien ta fenêtre génère des messages en permanence jusqu'à saturer la message queue, ce qui fait que 1) PeekMessage ne retourne jamais zero 2) il y a des messages non traités forcement.
Je te crois quand tu dit que tu utilise MoveWindow à tour de bras.
Tu peut donner la config de ton pc (quel prossesseur et quel OS)?
Et je peut te filer mon adresse msn en mp pour que tu me donne le source de ton programme?

Commentaire de shuttleur le 30/06/2008 11:51:07

PeekMessage retourne zero visiblement, puisque je n'ai qu'un seul thread dans mon appli, et que cette boucle finit très bien, en moyenne en 50ms.

MoveWindow n'est pas sollicité si abondamment que ca, jamais dans une boucle, ni dans le WM_SIZE ou WM_MOVEWINDOW bien sur.
Par contre j'utilise des SetWindowPos dans le WM_SIZE pour placer mes boutons après un redimensionnement.
D'après msdn, SetWindowPos ne renvoie pas de message WM_SIZE à son tour, donc il n'y a à priori aucune restriction à l'utiliser dans WM_SIZE, non ?

C'est une méthode horrible, et je serais ravi de m'en passer.
Mais sans ca, j'ai des résultats aléatoires, même sur des pcs (très) puissants. (core2duo 2Ghz + Vista.)

Je t'aurais montré volontiers mon code source s'il n'était pas confidentiel (pour mon boulot)

J'ai compris et je prends bien note de tes conseils sur le double buffering, et les DIB, et je reverrai le traitement de mon WM_PAINT dès que possible.

Commentaire de _michel le 30/06/2008 12:03:19

"PeekMessage retourne zero visiblement, puisque je n'ai qu'un seul thread dans mon appli, et que cette boucle finit très bien, en moyenne en 50ms."
Mais si elle finit, c'est aussi le programme qui finit, non?
Normalement après une boucle de message il y a un return, ce qui fait que dès que la boucle finit (dans ton cas dès que PeekMesage ne trouve pas de message!), alors le programme finit (si c'est la fenêtre principale).

"Par contre j'utilise des SetWindowPos dans le WM_SIZE pour placer mes boutons après un redimensionnement." De toute façon tant que l'on envoie pas de message de façon récursive (envoyer un message WM_SIZE à soit même dans le traitement d'un message WM_SIZE) il ne doit pas y avoir de problème. Les bouttons sont des fenêtres filles, donc tu peut leur envoyer ce que tu veux, même appeler MoveWindow() je pense.

"C'est une méthode horrible, et je serais ravi de m'en passer."
Je n'ai pas dit que c'était horrible, car je ne connait pas cette méthode suffisament. Par contre personnellement je serai ravi de m'en passer.

Commentaire de shuttleur le 30/06/2008 14:24:53

"Mais si elle finit, c'est aussi le programme qui finit, non?"
Non, ma boucle principale des messages pour la fenetre mère est la première que j'ai montrée ci dessus (sans peekMessage).
La seconde boucle (avec peekMessage) est mise un peu n'importe où dans mon code, là où j'ai remarqué qu'il y en avait besoin, il n'y a pas de return après, c'est simplement au milieu du code.
C'est cette seconde boucle qui parait-il est l'équivalent du DoEvents de VB.

"De toute façon ... appeler MoveWindow() je pense."
Oui tout à fait, j'ai parlé trop vite, aucun risque de ce côté.

Cette appli a vraiment été codée dans le souci de la performance, je ne peux pas croire qu'une appli relativement simple (toute skinnée + embarque un ActiveX qui joue une vidéo) détraque Windows à ce point.
Il me faudrait trouver  un soft qui puisse indiquer la "charge" de la pile des messages windows.

Commentaire de _michel le 30/06/2008 14:34:11

"La seconde boucle (avec peekMessage) est mise un peu n'importe où dans mon code, là où j'ai remarqué qu'il y en avait besoin, il n'y a pas de return après, c'est simplement au milieu du code."
Ca a l'air d'être une sacrée usine à gaz. Ces boucles là ne servent à rien à mon humble avis. Des boucles au beau milieu du code permettent peut-être de traiter des messages pendant le traitement d'un autre message (par exemple exiger un raffraichissement de l'écran par WM_PAINT pendant le traitement d'un message WM_SIZE). Mais pour ça il y a SendMessage() qui fonctionne très bien.

Il est toujours possible de tracer dans un fichier tous les messages interceptés.
Ou alors utiliser les hook peut-être.

Commentaire de shuttleur le 30/06/2008 14:37:50 9/10

Je crois avoir suffisamment dévié en HS ton post ;)

Merci de tes conseils, et bien sur aussi pour ta source, qui m'a fait découvrir les DIB.
Il va falloir que je les utilise absolument !
Je n'ai pas sur mon pc (Vista + core2duo 2Ghz) le problème que tu décris en conclusion.

Commentaire de _michel le 30/06/2008 14:42:32

Merci pour la note :-)

Commentaire de shorzy le 19/02/2009 21:53:53

Salut, le Code est peut être très bien, mais il y a due avoir une erreur lorsque tu as fait des Modif ...
J'ai des Erreur de Compil. (WinXP-VC2005)
S'il n'y a vraiment pas d'Erreur quand ti Copie-Colle le Code,
peux tu mettre un Ex_ STP que je voie au moins le Résultat Final..

Merci!

Commentaire de _michel le 19/02/2009 21:55:34

Je vois ça tout de suite...

Commentaire de _michel le 19/02/2009 22:07:08

Voila, ça m'aurais bien étonné de trouvé un seul warning, et effectivement il n'y en a pas.
J'ai même rajouté le projet en prime.
Bon courage avec ton compilateur...

Commentaire de shorzy le 20/02/2009 17:17:58

Problème :
J'utilise VC6 et je présise que je n'aie rien Télécharger (PSDK ATL...)

j'ai un Problème lors du Link !!!!!
"
LIBCD.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
Debug/main.exe : fatal error LNK1120: 1 unresolved externals
"
J'ai donc 3 Question :
Dois-je Linker des Fichiers *.Lib avant de Compiler ???
Comment Savoir s'il Faut Ajouter des Fichiers (LINK) ???
Que dois-je Faire pour que ca Marche ???

Si qqun peut me Sauver le Mise, MERCI.

Commentaire de _michel le 21/02/2009 00:04:21

Project->Properties->Linker->System->Subsystem = Windows

Quand tu crée un projet, il faut que tu spécifie à ton linker si tu veux une application graphique Windows ou une application executable dans la console. Visual Studio te le demande normalement.
Dans ton cas, il s'attend à un point d'entrée de type main() alors qu'en fait il faut WinMain() pour une application Windows.

Commentaire de shorzy le 25/02/2009 11:14:10

OKI,Comme je l'ai dis dans mon précédent Post j'ai VC2005
donc je ne peux pas utilisé ton projet (.dsw pour VC2005)

Par contre ton Explication m'a bien aidé :
- AVANT j'avais ouvert le .C puis j'ai Cliqué sur Executé
(Je pense qu'il a choisit par défaut une Execution en mode Console

- MAINTENANT j'ai Crée un Projet Win(Graph) et ça marche!!
Merci! Tu m'as mis sur la Voie !
(Mon bug peux aidé certains)

Pour le Programe :
Visuellement, je ne vois pas où est le Double-Buffering !
En regardant rapidement le code, Je pense qu'il s'agirait plutôt d'un 'Single'-Buffering ???

Je vous invite sur :
http://www.cppfrance.com/codes/DOUBLE-BUFFERING-MFC_48812.aspx
C'est en MFC. Pour voir rapidement le Double-Buffering :
Clic Gauche + Glisser la Souris
Presser la Touche 'F5' pour Activé en Direct le Double-Buffering
N'Hésiter pas à laisser un Post sur ce que vous en Pensez
++

Commentaire de _michel le 25/02/2009 13:12:52

C'est un double buffering dans le sens qu'on n'affiche pas directement ce qu'on prépare. On prépare (ça peut mettre du temps, j'ai des exemples où on met 1mn à préparer l'image), et on affiche tout d'un coup. C'est exactement le principe des jeux vidéos : l'image en préparation est stockée dans un espace mémoire distinct de la mémoire vidéo affichée.
Pour cette source, ça ne se voit pas (j'ai réduit au minimum), mais si on devais réafficher le fond à chaque appui de touche puis afficher l'ellipse, on voit un scintillement si on n'utilise pas cette méthode.

Commentaire de _michel le 25/02/2009 13:23:53

J'ai regardé ta source. Même si je n'ai jamais fait de MFC, je crois avoir compris l'important : un bitmap est créé au début de l'affichage, il sert à la préparation de l'image, puis il est blité sur le DC de l'application, et enfin il est libéré. Si c'est bien ça, la seule différence avec ma méthode, c'est que le bitmap n'existe que pendant l'affichage. Après, ça dépend de ce qu'on veut faire : si on veut pouvoir modifier l'image à tout moment, on aura intérêt à utiliser ma méthode, si on veux économiser des ressources ce sera plutot la tienne. Mais la mienne à tout de même un avantage certain : on est pas obligé de tout recalculer à chaque affichage, il suffit juste de changer ce que l'on veut. Prenons l'exemple d'un jeu d'échec. Si un joueur bouge une pièce, il suffit de redessiner la case de départ et celle d'arrivée sur le bitmap.

Commentaire de jeron le 08/08/2010 14:16:25

et si l'on veut incoporer dans ce projet une winform crée avec vsc++2008 ? comment on fait s'il vous plait ?
merci d'avance

Commentaire de _michel le 12/08/2010 11:32:56

Aucune idée, j'ai jamais touché à ces [mode troll on] conneries [mode troll off]. Renseigne-toi pour savoir comment gérer le graphisme avec cette technologie. Je doute que le GDI soit approprié. Bon courage!

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

Tracer graphique en fonction d'un tableau [ par Bobbix ] Bonjour,Dans le cadre d'un projet, j'ai besoin de votre aide sur le graphisme en WIN32. Nous avons réalisé une carte électronique d'un oscilloscope qu include GDI [ par Malkuth ] Bonjour,Voilà je voudrais comprendre je créer un projet (Application console WIN32) sous visual studio 2005 je rajoute #include &lt;gdiplus.h&gt;je co win32 interface graphique devcpp [ par Stephane ] Salut a tous Voila je fait le pas progressif Visual Basic vers CPP (j'utilise devcpp). Je commence &#224; """"""maitriser"""""" les class et pointeur Win32 affichage graphique fuite mémoire [ par _Jonathan ] Bonjour a tous,j'ai fait une petite application affichant un graphique. Mais lorsque je le lance, le programme me bouffe toutes les ressources sous wi Problème affichage Double buffering [ par _Jonathan ] Bonjour,Dans une fenetre windows, je crée un objet nommé graphique a l'aide de WNDCLASS (CreateWindow(..."graphique"...)). Dans cette zone, je dessine augmentation objets GDI [ par neomorpheus01 ] Bonjour,Je me pose une question à propos de la gestion des objets GDI dans une application MFC (type boite de dialogue) développée sous VC++6 Quand j' Clignotement graphique [ par sephiro ] je travail en C/C++ Win32 (no MFC)J'ouvre un context de peripherie HDC, je dessine des lignes, copies des BITMAP, efface des zones or j'ai un effet de projet win32 application [ par moumouteb ] Salut. Après avoir lut les éloges de Visual C++ 2005, je viens de passer dessu. Mais qu'elle est l'équivalent sous VC++ 2005 de : projet win32 applic cherche code source d'un application graphique de chute de balle [ par prado ] salut les amis je suis un jeune etudiant au senegal debutant en programmation ; j'ai un probeme avec un progamme en c , avec affichage graphique . en besoin d'aide pour programme d'affichage graphique [ par prado ] salut je suis un jeune etudiant senegalais debutant en informatique . j'ai un probleme avec mon programme .le libele est le suivant : on souhaite simu


Nos sponsors


Sondage...

CalendriCode

Mai 2012
LMMJVSD
 123456
78910111213
14151617181920
21222324252627
28293031   

Consulter la suite du CalendriCode

Photothèque

A découvrir



 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 2,324 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales