Region à partir d'un bitmap

0/5 (13 avis)

Snippet vu 6 665 fois - Téléchargée 37 fois

Contenu du snippet

Cette fonction permet d'avoir une region (de type HRGN) composée des pixels non noirs d'un bitmap (HBITMAP), afin de faire une fenetre avec une forme non rectangulaire.

On trouve pas mal de codes-sources comme celui-ci sur Internet, mais soit ils sont trop lents, soit ils ne marchent pas! Je l'ai appris à mes dépens!
Déçu, j'en ai fait un moi-même (en m'inspirant d'un source que j'avais trouvé sur le net qui ne marchait pas).
Celui-ci est très rapide (une dizaine de millisecondes, meme pour une grosse image)

Vous pouvez, pour tester la fonction:
-dessiner une image non recangulaire sur fond en teinte unie.
-dans le code, créer une fenetre sans bordure, sinon ça aura des conséquences étranges
-charger le bitmap dans une variable HBITMAP (grâce à LoadImage par exemple)
-Utiliser la fonction BmpToRgn avec en parametre le bitmap et la couleur de fond de l'image
-Utiliser SetWindowRgn pour affecter la region ainsi créee à la fenêtre
-utiliser BitBlt pour copier le bitmap sur la fenetre chaque fois qu'elle à besoin d'être redessinée

Source / Exemple :


HRGN BmpToRgn (HBITMAP hBmp, COLORREF cTransparentColor = 0)
{
#define ALLOC_UNIT 100
	HRGN hRgn = NULL;
	if (!hBmp) return 0; // si bitmap invalide retourne
	BITMAP bm;
	GetObject(hBmp, sizeof(bm), &bm); // met les infos d'en tete du bitmap dans bm
	UINT siz=bm.bmWidth*bm.bmHeight*4; // enregistre la taille des donnes de l'image
	char *lpBmpBits=(char*)LocalAlloc(LMEM_FIXED,siz); // fait de la place pour les bits du bitmap
	GetBitmapBits(hBmp,siz,lpBmpBits); // obtient les bits de l'image dans l'espace qu'on a reservé
	bm.bmBits=lpBmpBits; // complete la strucutre bm avec les bits
	while (bm.bmWidthBytes % 4) bm.bmWidthBytes++; // bmWidthBytes doit être divisible par 4

	DWORD maxRects = ALLOC_UNIT;
	HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects));
	RGNDATA *pData = (RGNDATA *)GlobalLock(hData);
	pData->rdh.dwSize = sizeof(RGNDATAHEADER);
	pData->rdh.iType = RDH_RECTANGLES;
	pData->rdh.nCount = pData->rdh.nRgnSize = 0;
	SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);

	BYTE *p32 = (BYTE *)bm.bmBits;
	for (int y = 0; y < bm.bmHeight; y++) // parcourt toutes les lignes de l'image, de haut en bas
	{
		for (int x = 0; x < bm.bmWidth; x++) // parcourt tous les pixels de la ligne, de gauche à droite
		{
			// Recherche une suite continue de pixels non transparents
			int x0 = x;
			LONG *p = (LONG *)(p32 + 4*x);
			while (x < bm.bmWidth)
			{
				if ((unsigned)*p==cTransparentColor)
					break; // ce pixel est transparent
				p++;
				x++;
			}
			if (x > x0)
			{
				// ajoute les pixels (de (x0, y) à (x, y+1)) à la region en tant que rectangle
				if (pData->rdh.nCount >= maxRects)
				{
					GlobalUnlock(hData);
					maxRects += ALLOC_UNIT;
					hData = GlobalReAlloc(hData, sizeof(RGNDATAHEADER) + 
						(sizeof(RECT) * maxRects), GMEM_MOVEABLE);
					pData = (RGNDATA *)GlobalLock(hData);
				}
				RECT *pr = (RECT *)&pData->Buffer;
				SetRect(&pr[pData->rdh.nCount], x0, y, x, y+1);
				if (x0 < pData->rdh.rcBound.left)
					pData->rdh.rcBound.left = x0;
				if (y < pData->rdh.rcBound.top)
					pData->rdh.rcBound.top = y;
				if (x > pData->rdh.rcBound.right)
					pData->rdh.rcBound.right = x;
				if (y+1 > pData->rdh.rcBound.bottom)
					pData->rdh.rcBound.bottom = y+1;
				pData->rdh.nCount++;

				// Il parait que sous Windows 98, ExtCreateRegion() ne marche pas s'il y a trop de rectangles
				// Pas de panique: on construit la region en deux fois
				if (pData->rdh.nCount == 2000)
				{
					HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);
					if (hRgn)
					{
						CombineRgn(hRgn, hRgn, h, RGN_OR);
						DeleteObject(h);
					}
					else
						hRgn = h;
					pData->rdh.nCount = 0;
					SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
				}
			}
		}
		// On passe à la ligne suivante
		p32 += bm.bmWidthBytes;
	}
	// On cree la region
	// (et, s'il y avait plus de 2000 rectangles, on la combine avec celle  créee precedemment)
	HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);
	if (hRgn)
	{
		CombineRgn(hRgn, hRgn, h, RGN_OR);
		DeleteObject(h);
	}
	else hRgn = h;
	LocalFree((HLOCAL)lpBmpBits);
	return hRgn;
}

A voir également

Ajouter un commentaire Commentaires
MangaII Messages postés 129 Date d'inscription dimanche 9 décembre 2001 Statut Membre Dernière intervention 12 janvier 2009
3 mai 2003 à 09:17
Pas mal le principe !!!
Mais le fait de passer toute l'image point par point ? c pas un peu long ?
Y'a auncune fonction de l'API qui fé ca automatiquement ?
cs_langedechu Messages postés 20 Date d'inscription mercredi 2 avril 2003 Statut Membre Dernière intervention 12 mai 2003
3 mai 2003 à 11:57
Super Ca marche tres bien.
Perso Ca fait un moment que je cherchais comment faire mais comme tu la dit ce que j'ai trouvé sur le net étais trops long en temps
d'execution.

Concernant le Message de MangaII : c peut etre long de passer l image poit par point au niveaux code ( en taille ) mais ca permet d avoir un temps d'execution le plus petit possible au niveau du traitement de l'image.
cs_langedechu Messages postés 20 Date d'inscription mercredi 2 avril 2003 Statut Membre Dernière intervention 12 mai 2003
3 mai 2003 à 13:16
j ai plein de bug graphique avec windows quand je fais une fenetre non carré.
par exemple si j ai une fenetre dejas ouverte ( style exploreur ou meme un autre programme ) et que je clic dessus (l ancienne fenetre)
je ma fenetre non carré qui s'efface et la partie efface reste dessiner sur l autre fenetre
cs_langedechu Messages postés 20 Date d'inscription mercredi 2 avril 2003 Statut Membre Dernière intervention 12 mai 2003
3 mai 2003 à 13:34
pour resoudre le bug si utilisation de mfc :
bouton droit sur la boite de dialogue en lever l option transparent.

puis sur définir :
OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hBrush = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

switch (nCtlColor)
{

case CTLCOLOR_DLG : // pour le fond
hBrush = (HBRUSH)GetStockObject(NULL_BRUSH); // transparent
break ;

} ;
return hBrush ;

}
cs_anthraxx Messages postés 47 Date d'inscription mardi 19 mars 2002 Statut Membre Dernière intervention 9 juin 2004
3 mai 2003 à 20:24
en reponse aux commentaires sur la vitesse d'execution:
il faut savoir que en effet la fonction utilise des instructions "for" imbriquées qui peuvent paraitre longues; cepandant, l'interieur de ces boucles n'est constitué que de code C++ et de manipulations basiques avec des pointeurs, ce qui est tres rapide, c++ etant connu pour cela.

Les autres codes sont plus lents, car s'il utilisent aussi des boucles imbriquées, celles-ci contiennent des appels aux API, comme GetPixel, CreateRectRgn et CombineRgn, et ces fonctions, elles, sont beaucoup plus lentes que des courtes operations entre pointeurs.

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.