Comment afficher des bitmaps avec des options

Description

Il est facile d'afficher une bitmap. Maintenant il est parfois utile de dessinner une partie d'une bitmap, comme par exemple pour faire des bitmaps transparentes, bon ici j'ai pris l'exemple d'une loupe : on a une loupe et donc on veut afficher une bitmap loupe mais on ne veut pas que l'interieur et la contour de la bimtap loupe s'afficher a l'ecran.Donc voici une source qui permet de voir comment afficher des bimtaps avec quelques options ... vous pouvez choisir l'operation qui sera effectuee avec l'ecran (deux operations a choisir)

je donne que le bout de code relatif a cela, le reste vous le trouverez dans les zip ... :)

Source / Exemple :


//----------------------------------------------------------
//	BITMAP FUNCTION
//----------------------------------------------------------
// notre structure de notre bitmap
// il y a toutes les informations necessaires pour effectuer
// les operations voulues
// bon voila a quoi correspondent les noms, les prefixes en disent long aussi ...
// - Mem          : memoire du resultat en bitmap
// - Image        : l'image que l'on veut afficher
// - Mask         : ceci est le mask (bitmap monochrome) pour identifier le fonc du devant (back/foreground)*
// - Background   : concerne le fond de l'image (a definir par une fonction lors de la creation de notre <BMP_AML>)
// - Foreground   : concerne le devant de l'image (si ce n'est pas du fond, c'est au devant)
typedef struct tagMY_BITMAP_AML
  {
  // taille reelle de l'image
  int       cxImage,
            cyImage;
  // taille a l'ecran de l'image
  int       cx,
            cy;
  // tous les DC
  HDC       hdcMem,
            hdcImage,
            hdcMask,
            hdcBackground,
            hdcForeground;
  // les handles de bitmap
  HBITMAP   bmpMem,
            bmpImage,
            bmpMask,
            bmpBackground,
            bmpForeground;
  // on garde en memoire les anciennes bitmaps selectionnees dans les DC
  HBITMAP   oldBmpMem,
            oldBmpImage,
            oldBmpMask,
            oldBmpBackground,
            oldBmpForeground;
  // on peut "verrouiller" l'affichage du fond ou du devant
  // cela peut sevir eventuellement pour faire de la transparence, et d'autres choses ...
  BOOL      bLockBackground,
            bLockForeground;
  // les operations de dessin
  DWORD     rasterOpBackground,rasterOpForeground;
  }MY_BITMAP_AML,*P_MY_BITMAP_AML,**PP_MY_BITMAP_AML;
//----------------------------------------------------------
// cette fonction cree un bitmap 'fait maison'
// elle permetera de faire des operations dessus du genre transparence
// pour cela la bitmap est le 'devant' de notre bitmap, cependant l'on doit
// definir ou est le fond (ou par exemple la zone de transparence)
// pour cela on doit donner une fonction qui dit si ce pixel appartient
// au fond ou au devant de la bitmap
// cette fonction peut choisir de definir une seule ou plusieurs
// couleurs comme fond pour l'image
// on a besoin aussi de l'<hdc> pour avoir des compatibleDCs
P_MY_BITMAP_AML myCreateBitmapAml(HDC hdc,int cx,int cy,HBITMAP hBmp,int (*isForeground)(COLORREF))
{
int               w,h;            // width, height
int               i,j;            // indices
P_MY_BITMAP_AML   bmpAml;         // notre bitmap
BITMAP            infoBmp;        // struture d'informations sur la bitmap
BITMAPINFO        paramBmp;       // parametres de la bitmap <bmp>
COLORREF          *dataBmp;       // zone de donnees de la bitmap <bmp>
COLORREF          *pLineBmp;      // pointeur sur un element de <dataBmp>
char              *dataMask;      // zone de donnees de la bitmap <maskBmp>
char              *pLineMask;     // pointeur sur un element de <dataMask>
int               sizeLineAligned;// taille en octets d'un ligne pour <maskBmp> aligne sur 32 bits
int               sizeLine;       // taille en octets d'un ligne pour <maskBmp>

// on recupere les informations de la bitmaps
if(0 == GetObject(hBmp,sizeof(BITMAP),(LPVOID)(&infoBmp)))
  {
  return NULL;
  }
// on s'assure qu'il y a des couleurs du type COLORREF
Assert(1                == infoBmp.bmPlanes);
Assert(32               == infoBmp.bmBitsPixel);
Assert(infoBmp.bmWidth  == ((infoBmp.bmWidthBytes+3)/4));

// on recupere la hauteur et la largeur
w                           = infoBmp.bmWidth;
h                           = infoBmp.bmHeight;
// on alloue notre bitmap, puis on initialise ses champs
bmpAml                      = Malloc(MY_BITMAP_AML,1);
bmpAml->hdcImage            = CreateCompatibleDC(hdc);
bmpAml->bmpImage            = hBmp;
bmpAml->cxImage             = w;
bmpAml->cyImage             = h;
bmpAml->cx                  = cx;
bmpAml->cy                  = cy;
bmpAml->bLockBackground     = FALSE; // par defaut, on affiche le fond
bmpAml->bLockForeground     = FALSE; // par defaut, on affiche le devant
bmpAml->rasterOpBackground  = SRCCOPY;
bmpAml->rasterOpForeground  = SRCCOPY;

// maintenant on cree la bitmap de mask, i.e. qui determine
// si le pixel appartient au fond ou au devant de l'image
// c'est la fontion <isForeground> qui fait la sepration background/foreground
// ATTENTION : sachant qu'il nous faut 2 octets pour 16 pixels, il faut
// prevoir assez de place, voire plus, de memoire pour les pixels
// c'est pourquoi il faut prendre le diviseur de 16 superieur au nombre de pixels sur une ligne
// pour cela on ajoute 15 (=16-1) avant de diviser pas 16 (arrondie superieure)
// (help : "Each scan line in the rectangle must be word aligned")
sizeLineAligned = ((w + 15) >> 4) << 1; // <=> 2*( (w+15)/16 )
sizeLine        = (7 + w) >> 3;         // <=> (7+w)/8
dataMask        = Malloc(char,sizeLineAligned*h);

// on initialise pour recevoir les bits de <bmp>
paramBmp.bmiHeader.biSize              = sizeof(paramBmp.bmiHeader);
paramBmp.bmiHeader.biWidth             = w;
paramBmp.bmiHeader.biHeight            = -h;                    // ici la valeur opposee, est pour specifier que l'origine est en haut a gauche de la bitmap
paramBmp.bmiHeader.biPlanes            = infoBmp.bmPlanes;
paramBmp.bmiHeader.biBitCount          = infoBmp.bmBitsPixel;   // on veut forcement une couleur du type COLORREF
paramBmp.bmiHeader.biCompression       = BI_RGB;                // couleurs RGB, pas de compression
paramBmp.bmiHeader.biSizeImage         = w*h*sizeof(COLORREF);
paramBmp.bmiHeader.biXPelsPerMeter     = 1;
paramBmp.bmiHeader.biYPelsPerMeter     = 1;
paramBmp.bmiHeader.biClrUsed           = 0;
paramBmp.bmiHeader.biClrImportant      = 0;
// on alloue une zone ou recevoir les octets
dataBmp   = (COLORREF*)Malloc(COLORREF,paramBmp.bmiHeader.biSizeImage);
// on peut maintenant recevoir les informations
// i.e. on recupere les couleurs de la bitmap en type COLORREF (32 bits)
if(0 == GetDIBits(hdc,hBmp,0,h,dataBmp,&paramBmp,DIB_RGB_COLORS))
  {
  Error(("GetDIBits n'a pas marche"));
  }

// on decremente <pBmp> pour incerementer directement plus tard
pLineBmp      = dataBmp - 1;
pLineMask     = dataMask;
// on commence la boucle
// on y va, c'est fastidieux, mais il faut le faire une fois pour toute
for(i=0;i<h;i++)
  {
  COLORREF  *pBmp;
  char      *pMask;

  pBmp  = pLineBmp;
  pMask = pLineMask;
  // ici on doit remplir octets par octets (seulement les octets a remplir totatelement)
  for(j=0;j<w/8;j++)
    {
    char      byte;
    // on traite 8 pixels par iteration
    byte    = (char)
                    (
                    ((isForeground(*++pBmp) != 0) << 7)
                      |
                    ((isForeground(*++pBmp) != 0) << 6)
                      |
                    ((isForeground(*++pBmp) != 0) << 5)
                      |
                    ((isForeground(*++pBmp) != 0) << 4)
                      |
                    ((isForeground(*++pBmp) != 0) << 3)
                      |
                    ((isForeground(*++pBmp) != 0) << 2)
                      |
                    ((isForeground(*++pBmp) != 0) << 1)
                      |
                    ((isForeground(*++pBmp) != 0) << 0)
                    );
    // on l'enregistre dans <pMask>

  • pMask = byte;
// au pixel suivant ! pMask ++; } // ici on remplit les pixels qui ne sont pas completement remplis (donc cela va de 0 a 7 pixels) // rappel : w & 7 <=> w % 8 if(w & 7) // si il n'y a pas un multiple de 8 en largeur {
  • pMask = 0;
for(j=7;j>=7-(w&7);j--) { (*pMask) |= ((isForeground(*++pBmp) != 0) << j); } pMask ++; } // ligne suivante pLineMask += sizeLineAligned; pLineBmp += infoBmp.bmWidthBytes/sizeof(COLORREF); } // on n'a plus qu'a cree notre bitmap de transparence bmpAml->bmpMask = CreateBitmap( w, // taille identique a <bmp> ... h, // ... car on a un 0/1 pour chaque pixel de <bmp> 1, // c'est pour que la bitmap soit monochrome 1, // il nous faut un bit par pixel, car c'est monochrome, donc 0 ou 1 (void*)dataMask // le paquet de bits, donc de pixels ); Assert(NULL != bmpAml->bmpMask); // on a eu besoin de <dataMask> et de <dataBmp> juste pour initialiser les pixels // on peut et on doit maintenant les liberer Free(dataBmp); Free(dataMask); // on peut enfin selectionner la bitmap dans notre compatibileDC // si on ne peut pas recevoir nos informations (Help : "The bitmap identified by the hbmp parameter must not be selected into a device context when the application calls this function.") bmpAml->oldBmpImage = SelectObject(bmpAml->hdcImage,hBmp); // bon on a tous les champs de la structure a initialiser // il faut creer et selectionner des bitmaps dans les DCs <hdcBackground> et <hdcForeground> // car il en fiat pour subir les operations GDI genre <BitBlt> ou <StretchBlt> // pour Mask bmpAml->hdcMask = CreateCompatibleDC(hdc); Assert(NULL != bmpAml->hdcMask); bmpAml->oldBmpMask = (HBITMAP)SelectObject(bmpAml->hdcMask,bmpAml->bmpMask); // pour Mem bmpAml->hdcMem = CreateCompatibleDC(hdc); Assert(NULL != bmpAml->hdcMem); bmpAml->bmpMem = CreateCompatibleBitmap(hdc,cx,cy); Assert(NULL != bmpAml->bmpMem); bmpAml->oldBmpMem = (HBITMAP)SelectObject(bmpAml->hdcMem,bmpAml->bmpMem); // pour Background bmpAml->hdcBackground = CreateCompatibleDC(hdc); Assert(NULL != bmpAml->hdcBackground); bmpAml->bmpBackground = CreateCompatibleBitmap(hdc,cx,cy); Assert(NULL != bmpAml->bmpBackground); bmpAml->oldBmpBackground = (HBITMAP)SelectObject(bmpAml->hdcBackground,bmpAml->bmpBackground); // pour Foreground bmpAml->hdcForeground = CreateCompatibleDC(hdc); Assert(NULL != bmpAml->hdcForeground); bmpAml->bmpForeground = CreateCompatibleBitmap(hdc,cx,cy); Assert(NULL != bmpAml->bmpForeground); bmpAml->oldBmpForeground = (HBITMAP)SelectObject(bmpAml->hdcForeground,bmpAml->bmpForeground); // on a fini le boulot de l'initialisation return bmpAml; } // myCreateBitmapAml() //---------------------------------------------------------- // remet l'ancienne bitmap et supprimer si cela est demander celle courant // puis detruit le DC void myCleanDC(HDC hdc,HBITMAP old,BOOL bDestroyBmp) { HBITMAP bmp; // on remet la bonne bitmap bmp = SelectObject(hdc,old); // on detruit la bitmap // si on nous demande de faire ce boulot if(bDestroyBmp) { DeleteObject(bmp); } // puis enfin on detruit le DC DeleteDC(hdc); } // myCleanDC() // cette fonction detruit un bitmap sans detruire la bitmap 'source' ou 'fille' // qui a ete donnee par le fonction <myCreateBitmapAml> HBITMAP myDeleteBitmapAml(P_MY_BITMAP_AML bmpAml) { HBITMAP hBmp; // on sauve la veritable bitmap, que l'on doit retourner hBmp = bmpAml->bmpImage; // bon maintenant on supprimer les DC, mais avant cele on reselectionne // les anciennes bitmaps, et on supprimera les courantes (sauf <bmpImage>) myCleanDC(bmpAml->hdcImage ,bmpAml->bmpImage ,FALSE ); myCleanDC(bmpAml->hdcMem ,bmpAml->bmpMem ,TRUE ); myCleanDC(bmpAml->hdcMask ,bmpAml->bmpMask ,TRUE ); myCleanDC(bmpAml->hdcBackground ,bmpAml->bmpBackground ,TRUE ); myCleanDC(bmpAml->hdcForeground ,bmpAml->bmpForeground ,TRUE ); // et enfin on detruit l'allocation memoire Free(bmpAml); return hBmp; } // myDeleteBitmapAml() //---------------------------------------------------------- // premet de changer l'operation de dessin // on retourne l'ancien oparateur et on selectionne le nouveau DWORD myChangeRasterOpBackground(P_MY_BITMAP_AML bmpAml,DWORD rasterOp) { DWORD old; old = bmpAml->rasterOpBackground; bmpAml->rasterOpBackground = rasterOp; return old; } // myChangeRasterOpBackground() //---------------------------------------------------------- // premet de changer l'operation de dessin // on retourne l'ancien oparateur et on selectionne le nouveau DWORD myChangeRasterOpForeground(P_MY_BITMAP_AML bmpAml,DWORD rasterOp) { DWORD old; old = bmpAml->rasterOpForeground; bmpAml->rasterOpForeground = rasterOp; return old; } // myChangeRasterOpForeground() //---------------------------------------------------------- // permet de verrouiller ou non le fond de la bitmap // <bLock> doit valoir TRUE si oui, sinon FALSE // de plus cette fonction retourne l'ancienne valeur BOOL myLockBackground(P_MY_BITMAP_AML bmpAml,BOOL bLock) { BOOL old; old = bmpAml->bLockBackground; bmpAml->bLockBackground = bLock; return old; } // myLockBackGround() //---------------------------------------------------------- // permet de verrouiller ou non le devant de la bitmap // <bLock> doit valoir TRUE si oui, sinon FALSE // de plus cette fonction retourne l'ancienne valeur BOOL myLockForeground(P_MY_BITMAP_AML bmpAml,BOOL bLock) { BOOL old; old = bmpAml->bLockForeground; bmpAml->bLockForeground = bLock; return old; } // myLockForeGround() //---------------------------------------------------------- // on fait nos calculs dans des DCs intermediaires // on affiche que le resultat final dans <hdcMem> pour avoir un // meilleur rendu visuel, car il ne faut pas effectuer un operation // sur un DC a l'ecran (cligonotement, problemes de raifraichissement, ...) BOOL WINAPI myRecalcBitmapAml(HDC hdc,int x,int y,P_MY_BITMAP_AML bmpAml) { BOOL success,lockBackground,lockForeground; HDC hdcMem,hdcImage,hdcMask,hdcBackground,hdcForeground; DWORD dwRopForeground,dwRopBackground; int w,h,cx,cy; // on prend tous les HDC utiles hdcMem = bmpAml->hdcMem; hdcImage = bmpAml->hdcImage; hdcMask = bmpAml->hdcMask; hdcBackground = bmpAml->hdcBackground; hdcForeground = bmpAml->hdcForeground; // on prend les verrouillage lockBackground = bmpAml->bLockBackground; lockForeground = bmpAml->bLockForeground; // on recupere la taille w = bmpAml->cxImage; h = bmpAml->cyImage; cx = bmpAml->cx; cy = bmpAml->cy; // les raster ops dwRopBackground = bmpAml->rasterOpBackground; dwRopForeground = bmpAml->rasterOpForeground; // pour les valeurs de retours de tout les <BitBlt> success = TRUE; // on commence a faire notre tres lourde operation (8 BitBlt au maximum !!) // on gere le fond success &= BitBlt (hdcBackground , 0, 0, cx, cy, hdc , x, y, SRCCOPY); if(!lockBackground) success &= StretchBlt (hdcBackground , 0, 0, cx, cy, hdcImage , 0, 0, w, h, dwRopBackground); success &= StretchBlt (hdcBackground , 0, 0, cx, cy, hdcMask , 0, 0, w, h, 0x00220326); // <=> hdcBackGround & (~hdcMask) // on gere le devant success &= BitBlt (hdcForeground , 0, 0, cx, cy, hdc , x, y, SRCCOPY); if(!lockForeground) success &= StretchBlt (hdcForeground , 0, 0, cx, cy, hdcImage , 0, 0, w, h, dwRopForeground); success &= StretchBlt (hdcForeground , 0, 0, cx, cy, hdcMask , 0, 0, w, h, SRCAND); // on fusionne le devant au fond success &= BitBlt (hdcMem , 0, 0, cx, cy, hdcForeground , 0, 0, SRCCOPY); success &= BitBlt (hdcMem , 0, 0, cx, cy, hdcBackground , 0, 0, SRCPAINT); // on a fini le boulot, on retourne TRUE si tout c'est bien passe sinon FALSE return success; } // myRecalcBitmapAml() //---------------------------------------------------------- BOOL WINAPI myDrawBitmapAml(HDC hdc,int x,int y,P_MY_BITMAP_AML bmpAml) { int cx,cy; HDC hdcMem; // on prend le <hdcMem> hdcMem = bmpAml->hdcMem; // on recupere la taille cx = bmpAml->cx; cy = bmpAml->cy; // et on affiche return BitBlt(hdc,x,y,cx,cy,hdcMem,0,0,SRCCOPY); } // myDrawBitmapAml() //---------------------------------------------------------- // cette fonction definie le blanc comme etant la seule couleur du fond int whiteBackground(COLORREF color) { return color - RGB(255,255,255); } // whiteBackground()

Conclusion :


Bon j'aurais pu autiliser la fonction MaskBlt, le petit probleme c'est que sous Win98 ca ne marche pas, il faut se la reprogrammer, c'est long (c'est pourquoi ca clignote)

Codes Sources

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.