Question

Je dois créer un contrôle personnalisé pour afficher les images BMP avec canal alpha. L’arrière-plan peut être peint de différentes couleurs et les images sont ombrées, c’est pourquoi je dois vraiment "peindre". le canal alpha.

Quelqu'un sait-il comment le faire?

Je souhaite également, si possible, créer un masque à l'aide des informations du canal alpha pour savoir si la souris a été cliquée sur l'image ou sur la zone transparente.

Toute aide sera appréciée!

Merci.

Édité (JDePedro): Comme certains d’entre vous l’ont suggéré, j’essaie d’utiliser le mélange alpha pour peindre le bitmap avec le canal alpha. C’est juste un test que j’ai implémenté et où je charge un bitmap 32 bits à partir de ressources et j’essaie de le peindre à l’aide de la fonction AlphaBlend:

void CAlphaDlg::OnPaint()
{
    CClientDC dc(this);
    CDC  dcMem;
    dcMem.CreateCompatibleDC(&dc);

    CBitmap bitmap;
    bitmap.LoadBitmap(IDB_BITMAP);

    BITMAP BitMap;
    bitmap.GetBitmap(&BitMap);
    int nWidth = BitMap.bmWidth;
    int nHeight = BitMap.bmHeight;
    CBitmap *pOldBitmap = dcMem.SelectObject(&bitmap);

    BLENDFUNCTION m_bf;
    m_bf.BlendOp = AC_SRC_OVER;
    m_bf.BlendFlags = 0;
    m_bf.SourceConstantAlpha = 255;
    m_bf.AlphaFormat = AC_SRC_ALPHA;
    AlphaBlend(dc.GetSafeHdc(), 100, 100, nWidth, nHeight, dcMem.GetSafeHdc(), 0, 0,nWidth, nHeight,m_bf); 

    dcMem.SelectObject(pOldBitmap);

    CDialog::OnPaint();
}

Ceci est juste un test donc je mets le code dans le OnPaint du dialogue (j'ai aussi essayé la fonction AlphaBlend de l'objet CDC).

Les zones non transparentes sont peintes correctement, mais je passe en blanc lorsque le bitmap doit être transparent.

Toute aide ???

Ceci est une capture d'écran ... ce n'est pas facile à voir mais un rectangle blanc entoure le cercle bleu: texte alt http://img385.imageshack.us/img385/7965/alphamh8.png

Ok. J? ai compris! Je dois pré-multiplier chaque pixel pour la valeur alpha. Quelqu'un peut-il suggérer le moyen optimisé de le faire?

Était-ce utile?

La solution

La procédure habituelle consiste à utiliser une section DIB, une bitmap indépendante du périphérique, que vous pouvez modifier directement. Malheureusement, DIBSections n’est pas pris en charge par MFC: vous devez utiliser la fonction Win32 CreateDIBSection () pour l’utiliser.

Commencez par charger le bitmap au format RGBA 32 bits (soit quatre octets par pixel: un rouge, un vert, un bleu et un pour le canal alpha). Dans le contrôle, créez une section DIB de taille appropriée. Ensuite, dans la routine de peinture

  • Copiez les données bitmap dans les données bitmap de DIBSection, en utilisant l'octet de canal alpha pour fusionner l'image bitmap avec la couleur d'arrière-plan.
  • Créez un contexte de périphérique et sélectionnez-y la section DIB.
  • Utilisez BitBlt () pour copier du nouveau contexte de périphérique dans le contexte de périphérique de peinture.

Vous pouvez créer un masque à partir des données bitmap brutes en regardant simplement les valeurs du canal alpha. Je ne sais pas trop ce que vous demandez ici.

Autres conseils

Pour les futurs utilisateurs de Google, voici une fonction de pré-multiplication qui fonctionne. Notez que cela provient de http://www.viksoe.dk/code/alphatut1.htm.

inline void PremultiplyBitmapAlpha(HDC hDC, HBITMAP hBmp)
{
   BITMAP bm = { 0 };
   GetObject(hBmp, sizeof(bm), &bm);
   BITMAPINFO* bmi = (BITMAPINFO*) _alloca(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
   ::ZeroMemory(bmi, sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
   bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
   BOOL bRes = ::GetDIBits(hDC, hBmp, 0, bm.bmHeight, NULL, bmi, DIB_RGB_COLORS);
   if( !bRes || bmi->bmiHeader.biBitCount != 32 ) return;
   LPBYTE pBitData = (LPBYTE) ::LocalAlloc(LPTR, bm.bmWidth * bm.bmHeight * sizeof(DWORD));
   if( pBitData == NULL ) return;
   LPBYTE pData = pBitData;
   ::GetDIBits(hDC, hBmp, 0, bm.bmHeight, pData, bmi, DIB_RGB_COLORS);
   for( int y = 0; y < bm.bmHeight; y++ ) {
      for( int x = 0; x < bm.bmWidth; x++ ) {
         pData[0] = (BYTE)((DWORD)pData[0] * pData[3] / 255);
         pData[1] = (BYTE)((DWORD)pData[1] * pData[3] / 255);
         pData[2] = (BYTE)((DWORD)pData[2] * pData[3] / 255);
         pData += 4;
      }
   }
   ::SetDIBits(hDC, hBmp, 0, bm.bmHeight, pBitData, bmi, DIB_RGB_COLORS);
   ::LocalFree(pBitData);
}

Votre OnPaint devient alors:

void MyButton::OnPaint()
{
    CPaintDC dc(this);

    CRect rect(0, 0, 16, 16);

    static bool pmdone = false;
    if (!pmdone) {
        PremultiplyBitmapAlpha(dc, m_Image);
        pmdone = true;
    }

    BLENDFUNCTION bf;
    bf.BlendOp = AC_SRC_OVER;
    bf.BlendFlags = 0;
    bf.SourceConstantAlpha = 255;
    bf.AlphaFormat = AC_SRC_ALPHA;

    HDC src_dc = m_Image.GetDC();
    ::AlphaBlend(dc, rect.left, rect.top, 16, 16, src_dc, 0, 0, 16, 16, bf);
    m_Image.ReleaseDC();
}

Et le chargement de l'image (dans le constructeur de votre contrôle):

if ((HBITMAP)m_Image == NULL) {
    m_Image.LoadFromResource(::AfxGetResourceHandle(), IDB_RESOURCE_OF_32_BPP_BITMAP);
}

Vous devez créer un mélange de fondu avec votre couleur d'arrière-plan, puis supprimer le caractère alpha canal pour le peindre au contrôle.

Le canal alpha ne doit contenir que tous les 4 octets de votre image. Vous pouvez l'utiliser directement pour votre masque ou simplement copier tous les 4 octets dans une nouvelle image de masque.

Il est très facile de peindre avec la fonction AlphaBlend .

En ce qui concerne votre masque, vous devez récupérez les bits du bitmap et examinez l'octet du canal alpha pour chaque pixel qui vous intéresse.

Une façon optimisée de prémultiplier les canaux RVB avec le canal alpha consiste à configurer un tableau [256] [256] contenant les résultats de multiplication calculés. La première dimension est la valeur alpha, la seconde est la valeur R / V / B, les valeurs du tableau sont les valeurs pré-multipliées dont vous avez besoin.

Une fois ce tableau correctement configuré, vous pouvez calculer la valeur dont vous avez besoin comme suit:

R = multiplicationLookup[alpha][R];
G = multiplicationLookup[alpha][G];
B = multiplicationLookup[alpha][B];

Vous êtes sur la bonne voie, mais vous devez résoudre deux problèmes.

Commencez par utiliser :: LoadImage (.. LR_CREATEDIBSECTION ..) à la place de CBitmap :: LoadBitmap. Deuxièmement, vous devez "pré-multiplier" Les valeurs RVB de chaque pixel d'une image bitmap par rapport à leur valeur A. C’est une exigence de la fonction AlphaBlend, voir la description d’AlphaFormat à la cette page MSDN . T

Le lpng a un code de travail qui effectue la prémultiplication des données DIB.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top