Region à partir d'un bitmap

Soyez le premier à donner votre avis sur cette source.

Snippet vu 6 476 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
Messages postés
59
Date d'inscription
samedi 1 janvier 2005
Statut
Membre
Dernière intervention
4 avril 2007

je pense qu'il faudrait remplacer la ligne
LONG *p = (LONG *)(p32 + 4*x);

par DWORD *p = (DWORD *)(p32 + 4*x);

car sur certains processeurs un LONG ne fait pas 4 octets (je n'ai pas encore testé)
Messages postés
571
Date d'inscription
samedi 3 avril 2004
Statut
Membre
Dernière intervention
5 décembre 2013

Salut anthrax et merci de repondre si vite je mattendais meme pas a une reponse !!!
Vla le screen shoot http://membres.lycos.fr/nsdbgt100/defo.JPG
Mais g bien fait le dialog sans bordure ni barre de titre, d'ailleur ca marche chez moi sans aucun probleme. Tandis que chez lui meme mirc meme dll( c la ou g mit ta fonction) meme theme d'ecran on a ce resultat.
La seule difference c'est ke lui a XP Gold et mo XP pro classique !!!
Messages postés
47
Date d'inscription
mardi 19 mars 2002
Statut
Membre
Dernière intervention
9 juin 2004

je n'ai pas très bien compris ce qu'il se passe chez ton ami, faudrait un screenshot. Mais peut-être que ça vient du fait que ta fenêtre n'est pas sans bordure et sans barre de titre?

Sinon je peux te dire que si c'est juste un probleme de décalage je ne pense pas que ça vienne de ma fonction.
Par contre si la forme de la région obtenue n'est pas du tout la même que le dessin dans le bitmap, alors je veux bien croire qu'il y a un probleme...
Messages postés
571
Date d'inscription
samedi 3 avril 2004
Statut
Membre
Dernière intervention
5 décembre 2013

J'ai un bug bizarre, chez moi ca marche sans probleme mais chez un ami (windows xp avec le meme theme windows que moi ). Le dialog est mal decoupé c comme si il y avait un decalge pourtant kan je blitte le bitmap apres le decoupage celui ci se met a la bonne place ( a par ke le decoupage ne ressemble a rien.
PS : en fait c dans une dll et c un aurtre programme ki cré le dialog et qui le gere !!
Messages postés
47
Date d'inscription
mardi 19 mars 2002
Statut
Membre
Dernière intervention
9 juin 2004

En effet, l'intêret du code réside dans l'optimisation des boucles.
Tu ne remarques probablement pas de différence avec ta "méthode brutale" car tu dois avoir une machine puissante.
A l'époque où j'avais fait cette fonction, j'avais essayé la méthode brutale aussi, mais elle mettait plusieurs secondes... un temps de chargement inacceptable. Aujourd'hui les ordinateurs sont plus puissants.
Je conseille quand même d'utiliser ma fonction, car même si la différence ne se voit pas, il est inutile de gacher la puissance du pc... Le jour ou tu auras installé plein de programmes qui marchent en arrière plan, qui ralentissent beaucoup, tu verras peut-être la différence.
Par contre, j'admet que mon code est beaucoup plus volumineux, donc si la taille du code est essentielle c'est toi qui choisis.

quant à la limitation du nombre de regions, je ne sais pas. Sur windows 98 il y a bien une limitation mentionnée en commentaire dans mon code, mais pour les version postérieures je ne crois pas, et en plus, la limite est de l'ordre de 2000, pas de 100.
tu devrais revoir ton code de plus près

--anthraxx
Afficher les 13 commentaires

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.