IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

MFC Discussion :

StretchBlt pour resizer un bmp ne fonctionne pas à l'écran


Sujet :

MFC

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    76
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2006
    Messages : 76
    Points : 40
    Points
    40
    Par défaut StretchBlt pour resizer un bmp ne fonctionne pas à l'écran
    Bonjour,

    Afin d'afficher une bannière par dessus ma barre de titre, j'utilise GDI et notamment la fonction StretchBlt(). Après quelques heures de tests, je me rends compte que cette fonction ne me redimensionne rien du tout .

    Une petite recherche sur le forum me permet d'invoquer la fonction

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
     
    int ret = GetDeviceCaps(dc, RC_STRETCHBLT);  // dc est un CWindowDC
    et là je constate que la valeur de retour est 0 , donc non supporté à priori.

    mes questions:
    - Comment activer le support de cette fonction sur mon pc? dx9.0c est installé (p'têt rien à avoir?) et la carte graphique est une intégrée (Intel Q965 express).

    - S'il n'y a réellement pas moyen d'activer cette fonction, existe t il une méthode (de préférence tout aussi "simple") qui fonctionnerait sur tous les pc?

    Voici à titre indicatif le code utilisé:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    		CWindowDC dc(this);
     
    		CRect rc;
            GetWindowRect(rc);
            // Size of menu bar (non-client area) is smaller
    		rc.bottom = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME);
     
    		CBitmap Bitmap;
    		CDC MemDC;
     
    		Bitmap.LoadBitmap(IDB_BITMAP4); 
    		BITMAP InfosBmp; 
    		Bitmap.GetBitmap(&InfosBmp);
    		MemDC.CreateCompatibleDC(&dc);
    		MemDC.SelectObject(&Bitmap); en memoire
     
    		/*dc.BitBlt( 0, -15, InfosBmp.bmWidth, InfosBmp.bmHeight,
    						 &MemDC,
    						 0,0,
    						 SRCCOPY);*/
     
    		CSize Size(InfosBmp.bmWidth, InfosBmp.bmHeight);
    		dc.DPtoLP(&Size);
    		dc.SetStretchBltMode( HALFTONE);
     
    		int hihihi = GetDeviceCaps(dc, RC_STRETCHBLT);
     
    		dc.StretchBlt( 0, -15,
    						 Size.cx,Size.cy,
    						 &MemDC,
    						 0, 0,
    						 Size.cx,Size.cy,//InfosBmp.bmWidth, InfosBmp.bmHeight,
    						 SRCCOPY);

    Merci pour votre aide.

    Francois

  2. #2
    Membre confirmé Avatar de stephdim
    Profil pro
    Inscrit en
    Août 2007
    Messages
    462
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 462
    Points : 521
    Points
    521
    Par défaut
    salut,

    déjà c'est pas comme ça qu'il faut vérifier :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int ret = GetDeviceCaps(dc, RC_STRETCHBLT);  // dc est un CWindowDC
    mais plutot comme ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if (GetDeviceCaps(dc,RASTERCAPS)&RC_STRETCHBLT)
      AfxMessageBox(_T("StretchBlt() supporté !"));
    ensuite, dans le code que tu postes, tu ne fais aucun changement de taille dans les params de StretchBlt() ... d'autant plus que je ne vois pas l'intéret du DPtoLP ...

    je suppose que ta question est en relation avec ton autre thread : http://www.developpez.net/forums/sho...d.php?t=534199

    le dessin doit se faire dans le message WM_NCPAINT qui est la procédure pour dessiner une partie "non cliente", c'est à dire le contour de la fenetre, le caption ... et non dans OnDraw() qui correspond à WM_PAINT

    ce message doit etre intercepté par la fenetre concernée qui possède le caption ... et non la vue qui n'est qu'une fenetre "child"

    @+

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    76
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2006
    Messages : 76
    Points : 40
    Points
    40
    Par défaut
    Bonjour stephdim,

    tout d'abord, merci pour ta réponse, les questions sont effectivement en relation avec mon autre thread, il m'a fallu la journée d'hier pour comprendre *un peu* mieux le fonctionnement, et l'utilisation du message NCPAINT/CWindowDC...

    Effectivement, en modifiant le test, la fonction stretchblt() est bel et bien supportée..

    Concernant ta remarque sur cette fonction, je ne comprends pas bien ce que je dois modifier:
    Les 4 premiers paramètres sont le rectangle de destination, donc je devrais plutôt utiliser
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    	CRect rc;
            GetWindowRect(rc); 
     
            ==>  rc.width() et rc.height()  // pour le 3ème et 4ème paramètre
    mon bmp disparait quand je fais celà.
    Les 4 paramètres pour le rectangle source, ceux là doivent être fixe non? s'il s'agit de la taille de mon bitmap?


    merci pour ton aide,

    Francois

    ----------------------------

    EDIT:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    dc.StretchBlt( 0, 0,
    			rc.right,rc.bottom,
    						 &MemDC,
    						 0, 0,
    						 InfosBmp.bmWidth, InfosBmp.bmHeight,
    						 SRCCOPY);
    avec ce code, ça fonctionne très bien
    Par contre l'image est pas super jolie mais bon.. je vais commencer par mettre en place un double buffering pour l'affichage, on sait jamais...

    Une ou deux autres questions pour continuer:
    - Comment puis je m'assurer que ma bitmap sera toujours au premier plan? Actuellement, au démarrage de l'appli elle est cachée par la title bar et il faut que je bouge un peu la fenêtre pour qu'elle vienne magiquement au premier plan.. puis je appelé la fonction qui traite le message NCPAINT dans ma fonction ONDRAW() où existe t il une solution plus 'jolie'.
    - Le message NCPAINT me permet il de dessiner sur les bords de la fenêtre? ainsi que sur une éventuelle statusbar?

    Merci

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    76
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2006
    Messages : 76
    Points : 40
    Points
    40
    Par défaut
    Heu...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
            CWindowDC dc(this);
     
            GetWindowRect(rc);
     
    	rc.bottom = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME);
     
    	CDC MemDC;
    	MemDC.CreateCompatibleDC(&dc);
    	MemDC.SelectObject(&Bitmap); 
     
    	dc.SetStretchBltMode( HALFTONE);
    	dc.StretchBlt( 0, 0,
    			rc.right,rc.bottom,
    			&MemDC,
    			0, 0,
    			InfosBmp.bmWidth, InfosBmp.bmHeight,
    			SRCCOPY); 
     
           MemDC.DeleteDC();
    En procédant comme ceci, Est ce que j'utilise le double buffering?
    J'ai envie de dire que oui par l'utilisation d'un CDC intermédiaire, mais j'ai peur de mal comprendre la technique étant donné que mon image clignote quand je resize ma fenêtre :/.

    Au niveau du nettoyage de la mémoire, est ce que je suis bon? (car il parait que ca joue fortement )
    rc, bitmap et infobmp sont des attributs de ma classe.
    Je load la bitmap et prend ses info au niveau du constructeur et je ne fais QUE un bitmap.deleteobject() au niveau du destructeur.
    Est ce que j'oublie certaines choses?



    note: j'ai surchargé la fonction onerasebkgnd afin de ne pas effacer/redessiner en permanence..

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    BOOL CMainFrame::OnEraseBkgnd(CDC* pDC)
    {
    	// TODO: Add your message handler code here and/or call default
     
    	return false;
    }

    EDIT:
    En fait, le problème de scintillement n'apparait que sur la bmp qui est sur la title bar, le problème n'apparait pas au niveau de ma menu bar.
    Je pense que c'est l'affichage du titre de la frame qui fait que, une fois je dessine le bmp, une autre fois le titre. vais allez essayer de déactiver le titre...

  5. #5
    Membre confirmé Avatar de stephdim
    Profil pro
    Inscrit en
    Août 2007
    Messages
    462
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 462
    Points : 521
    Points
    521
    Par défaut
    En procédant comme ceci, Est ce que j'utilise le double buffering?
    non, c'est de l'affichage direct. Je ne pense pas que le double-buffering soit nécessaire pour juste afficher un bitmap. Ton probleme est autre

    Au niveau du nettoyage de la mémoire, est ce que je suis bon?
    non, il faut que tu déselectionnes le bitmap de ton Memory Device Context, comme ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    CBitmap *pOldBitmap=memdc.SelectObject(&bitmap);
     
    ....  // tu utilises le bitmap ici
     
    memdc.SelectObject(pOldBitmap);  // on restitue l'ancien bitmap pour éviter des GDI leaks
    pas la peine de faire memdc.DeleteDC(), le destructeur de la classe CDC s'en charge déjà pour toi. Idem pour le bitmap ...

    Mais la déselection est importante, sinon GDI leaks

    j'ai surchargé la fonction onerasebkgnd afin de ne pas effacer/redessiner en permanence..
    retourne plutot TRUE que FALSE, mais je vois pas trop le rapport avec ton objectif ...

    une fenetre est composée de deux parties :

    * une zone cliente qui est un rectangle dans lequel tu peux dessiner comme tu veux avec OnPaint(), OnDraw() ...
    * une zone non cliente qui est un rectangle qui contient la zone cliente, plus le contour, le titre, le menu de la fenetre ... cette zone est gérée par le systeme d'exploitation, classiquement on ne dessine pas dedans ...

    la zone cliente est dessinée lors du message WM_PAINT qui génère un WM_ERASEBKGND dès que tu récupère un PaintDC (par CPaintDC ou BeginPaint())

    la zone non cliente est dessinée lors du message WM_NCPAINT

    il faut donc intercepter ce message, créer un CWindowDC et dessiner avec ce Device Context. Le probleme c'est qu'il faut aussi gérer l'affichage systeme de la barre de titre, menu ...

    bon voici un petit exemple, pas testé, mais qui te mettra sur une piste:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
    void CMaFrameWnd::OnNcPaint()
    {
      CRgn *pRgn=CRgn::FromHandle((HRGN)GetCurrentMessage()->wParam);
      CDC *pDC=GetDCEx(pRgn,DCX_WINDOW|DCX_INTERSECTRGN);
     
      CRect rect;           // rectangle en coordonnées relatives au coin gauche supérieur de la fenetre
                            // a toi de définir les coordonnées selon l'endroit ou tu veux dessiner
     
      ...
     
      {
        CBitmap bitmap;     // objet bitmap, a toi de l'initialiser correctement
     
        ...
     
        CDC memdc;
        VERIFY(memdc.CreateCompatibleDC(pDC));
        CBitmap *pOldBitmap=memdc.SelectObject(&bitmap);
     
        pDC->StretchBlt( ... );
     
        memdc.SelectObject(pOldBitmap);
      }
     
      ReleaseDC(pDC);    // on n'en a plus besoin
     
      // ici on veut que le systeme redessine le reste de la fenetre sauf dans la zone ou nous avons dessiné
      CRgn rgn1;
      rgn.CreateRectRgnIndirect(rect);
      CRgn rgn2;
      rgn2.CombineRgn(pRgn,&rgn1,RGN_DIFF);  // on exclut le rectangle dessiné
     
      DefWindowProc(WM_NCPAINT,(WPARAM)rgn2.m_hObject,0);   // pour que le systeme redessine le reste de la fenetre
    }
    @+

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    76
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2006
    Messages : 76
    Points : 40
    Points
    40
    Par défaut
    Citation Envoyé par stephdim Voir le message
    non, c'est de l'affichage direct. Je ne pense pas que le double-buffering soit nécessaire pour juste afficher un bitmap. Ton probleme est autre
    Effectivement, je cerne mieux le problème, il faudrait que je désactive le dessin de la title bar (titre et bouton fonction (close,..) mais je cherche encore comment faire..


    non, il faut que tu déselectionnes le bitmap de ton Memory Device Context, comme ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    CBitmap *pOldBitmap=memdc.SelectObject(&bitmap);
     
    ....  // tu utilises le bitmap ici
     
    memdc.SelectObject(pOldBitmap);  // on restitue l'ancien bitmap pour éviter des GDI leaks
    pas la peine de faire memdc.DeleteDC(), le destructeur de la classe CDC s'en charge déjà pour toi. Idem pour le bitmap ...

    Mais la déselection est importante, sinon GDI leaks
    Ok.

    retourne plutot TRUE que FALSE, mais je vois pas trop le rapport avec ton objectif ...
    Bien en fait, j'ai lu je sais déjà plus où que celà pouvais réduire le scintillement...
    mais je vais retourner TRUE comme tu me le dis car ça ne change pas grand chose...
    Je ne suis pas sur mais si ca tombe, c'est le background de la zone client que je n'erase pas, donc effectivement, aucun intérêt pour moi..

    une fenetre est composée de deux parties :

    * une zone cliente qui est un rectangle dans lequel tu peux dessiner comme tu veux avec OnPaint(), OnDraw() ...
    * une zone non cliente qui est un rectangle qui contient la zone cliente, plus le contour, le titre, le menu de la fenetre ... cette zone est gérée par le systeme d'exploitation, classiquement on ne dessine pas dedans ...
    me doute

    (...)

    il faut donc intercepter ce message, créer un CWindowDC et dessiner avec ce Device Context. Le probleme c'est qu'il faut aussi gérer l'affichage systeme de la barre de titre, menu ...
    Il est vrai que c'est un problème. petit scénario pour expliquer ce qui semble être mon dernier problème:
    si je mets une autre fenêtre au premier plan devant ceque je dessine, en revenant, la zone noncliente est redessinée avec ma bitmap, MAIS une grosse partie de la title bar (90%) est redessinée par dessus..
    Y a t il un moyen d'empêcher le fait de redessiner la titlebar (juste la zone de titre)?

    note: j'affiche aussi un bitmap au dessus de mon menu et le comportement avec le menu est parfait, pas de scintillement ni rien..



    bon voici un petit exemple, pas testé, mais qui te mettra sur une piste:

    @+
    J'ai essayer avec ton exemple, j'obtiens le même résultat que sans jouer avec les régions à la fin. Sans doute à cause du titre..

    Une idée?


    merci beaucoup pour ton aide.

  7. #7
    Membre confirmé Avatar de stephdim
    Profil pro
    Inscrit en
    Août 2007
    Messages
    462
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 462
    Points : 521
    Points
    521
    Par défaut
    J'ai essayer avec ton exemple, j'obtiens le même résultat que sans jouer avec les régions à la fin. Sans doute à cause du titre..
    il faut peut etre aussi voir du coté de WM_NCACTIVATE

    c'est un message qui est utilisé pour changer l'apparence de la barre des titres à l'activation / désactivation

    quand je lis la MSDN sur ce message:

    The DefWindowProc function draws the title bar or icon title in its active colors when the wParam parameter is TRUE and in its inactive colors when wParam is FALSE.
    de ce que je comprend, le système dessine directement la barre de titre dans sa nouvelle apparence sans passer par WM_NCPAINT

    il faut donc peut etre dessiner la barre de titre dans les deux messages alors

    mais là c'est des suppositions, je n'ai pas encore eu l'occasion d'expérimenter ce probleme.

    l'effet de scintillement est du au fait que le systeme dessine et toi tu dessines par dessus. il faut donc exclure la région que tu veux dessiner avant de laisser le systeme dessiner ... si c'est bien fait, il n'y aura plus de scintillement.

    @+

Discussions similaires

  1. Réponses: 2
    Dernier message: 12/05/2015, 00h47
  2. Réponses: 2
    Dernier message: 01/03/2014, 10h19
  3. Aide pour un code qui ne fonctionne pas correctement
    Par NEC14 dans le forum Macros et VBA Excel
    Réponses: 7
    Dernier message: 05/03/2013, 09h23
  4. macro pour code barre qui ne fonctionne pas comme elle devrait
    Par scons dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 13/11/2009, 13h09
  5. [vb]sauvegarder en bmp ne fonctionne pas
    Par cd090580 dans le forum Windows Forms
    Réponses: 2
    Dernier message: 24/03/2007, 10h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo