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;
}
3 mai 2003 à 09:17
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 ?
3 mai 2003 à 11:57
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.
3 mai 2003 à 13:16
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
3 mai 2003 à 13:34
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 ;
}
3 mai 2003 à 20:24
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.