Region à partir d'un bitmap

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

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.