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

C++Builder Discussion :

problème avec GetDIB


Sujet :

C++Builder

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé

    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    137
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 137
    Par défaut problème avec GetDIB
    Bonjour,

    Mon but est d'imprimer une TBitmap sur le Canvas de TPrinter.
    J'utilise donc les méthodes GetDIBSizes et GetDIB pour convertir le bitmap en DIB (puis j'utilise la fonction StretchDIBits, comme recommandé ici)

    Cependant, sur Windows XP (et Window 2000 je crois) la fonction GetDIB échoue de manière assez aléatoire (assez rarement quand même). Ca fait un moment que je traine ce problème, et je ne suis pas le seul, mais je n'ai jamais trouvé LA solution... HELP !

  2. #2
    Membre expérimenté

    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    288
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Janvier 2003
    Messages : 288
    Par défaut
    J'ai beaucoup souffert avec les DIBs, je pense que le problème vient de la gestion de la mémoire par la carte vidéo (car c'est elle qui prends en charge le chargement conversion des images). Cela explique que cela fonctionne et parfois pas.
    Ce qui ne va pas dans l'exemple c'est GetMem, FreeMem.
    Voici ce que je fais à la place:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    pBitmapHeader = (BITMAPINFO*)::HeapAlloc(::GetProcessHeap(),NULL,iMemNeeded);
    ...
    ::HeapFree(::GetProcessHeap(),NULL,pBitmapHeader);
    ps Je n'utilise pas de TBitmap mais le principe est le même.

    yarp
    www.senosoft.com

  3. #3
    Membre confirmé

    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    137
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 137
    Par défaut
    Ok merci beaucoup pour la réponse Yarp.
    Je vais essayer t'intégrer ton astuce. Je serais très étonné si ça fonctionne mais tu as l'air de bien connaitre ce domaine.

    Sinon une autre solution serait de charger le DIB à partir d'un fichier BMP (sans passer par un TBitmap donc)...
    Et quelle classe utilises tu au lieu de TBitmap ?

    Encore merci

  4. #4
    Membre expérimenté

    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    288
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Janvier 2003
    Messages : 288
    Par défaut
    Une classe perso (mon appli traite les images et je me suis inspiré à l'origine de PaintLib) mais qui revient in fine à charger un BITMAPINFO (je charge le BITMPAINFOHEADER et les Bits et après je reconstruit les Dibs seulement quand j'en ai besoin pour ne pas laisser le gourmand Windows gérer lui-même les Handles . D'ou j'avais besoin de construire les Dibs.

    Comme je te le disais le caractère pseudo aléatoire provient de la différence de gestion de mémoire entre ton appli et celle de la carte vidéo, car quand tu alloue la mémoire pour un Dib c'est la carte vidéo qui prends en charge la conversion. Or les drivers de carte sont assez capricieux, pour le moins. HeapAlloc résoud le problème, car la mémoire "is not movable" et est Thread Safe (cf. Msdn), en bref ça fixe les drivers.
    http://msdn2.microsoft.com/en-us/library/aa366597.aspx

    Vu ce matin sur CodeGear et qui pourrait t'intéresser, "Printing a TImage":
    http://dn.codegear.com/article/22018

    Il y a du GetDiBits et du GlobalAlloc (que je te conseille de remplacer par les HeapAlloc comme ci-dessus), on touche au coeur du sujet.

    yarp
    www.senosoft.com

  5. #5
    Membre confirmé

    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    137
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 137
    Par défaut
    J'ai donc mis en place les HeapAlloc...

    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
    bool __fastcall DrawBitmapToPrinterCanvas(Graphics::TBitmap *pBitmap, TCanvas *pCanvas, TRect rect)
    {
        unsigned int headerSize, imageSize;
        BITMAPINFO *pBitmapHeader = NULL;
        void *pBitmapImage = NULL;
        int result = GDI_ERROR;
        try
            {
            GetDIBSizes(pBitmap->Handle, headerSize, imageSize);
            //pBitmapHeader = (BITMAPINFO *)malloc(HeaderSize);
            //??? pBitmapImage = (void *)malloc(ImageSize);
            pBitmapHeader = (BITMAPINFO*)::HeapAlloc(::GetProcessHeap(), NULL, headerSize);
            pBitmapImage = (void *)::HeapAlloc(::GetProcessHeap(), NULL, imageSize);
            if (GetDIB(pBitmap->Handle, pBitmap->Palette, pBitmapHeader, pBitmapImage))
                {
                pCanvas->Lock();
                result = StretchDIBits(pCanvas->Handle, rect.Left, rect.Top, rect.Width(), rect.Height(),
                              0, 0, pBitmap->Width, pBitmap->Height,
                              pBitmapImage, pBitmapHeader,
                              DIB_RGB_COLORS, SRCCOPY);
                pCanvas->Unlock();
                }
            }
        catch(...)
            {
            }
        //if (pBitmapHeader != NULL) free(pBitmapHeader);
        //if (pBitmapImage != NULL) free(pBitmapImage);
        if (pBitmapHeader != NULL) ::HeapFree(::GetProcessHeap(), NULL, pBitmapHeader);
        if (pBitmapImage != NULL) ::HeapFree(::GetProcessHeap(), NULL, pBitmapImage);
        return (result != GDI_ERROR);
    }
    Pour l'instant, je ne sais pas si le problème est résolu. En fait, je n'arrive pas à reproduire le problème sur mon PC avec l'ancien ou le nouveau code.

    Par contre j'aurai rapidement le retour de mes clients... En tout cas, merci énormément Yarp pour ton aide. J'ai passé pas mal d'heure à chercher une solution sur Internet et à retourner le problème dans tous les sens (fuite de mémoire ? problème de cerveau ?). Alors si ta solution fonctionne...

    A+

  6. #6
    Membre expérimenté

    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    288
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Janvier 2003
    Messages : 288
    Par défaut
    Imho I think so. Je distribue mon appli à des milliers d'exemplaires et si problème est en effet aléatoire (car dépends des cartes graphiques) je n'en ait plus entendu parlé depuis que j'utilise HeapAlloc. Si cela ne fonctionnait pas je te conseille en tout cas de travailler sur cette partie allocation de mémoire.
    Il est vrai que le problème est rarement évoqué, voire pas du tout, et la connaissance que j'en ait est purement empirique. J'ai remarqué que à chaque sortie d'un nouveau modèle de la NVidia GeForce le phénomène se produisait, et que dès leurs premiers patchs il était résolu => ma déduction.

    yarp
    www.senosoft.com

  7. #7
    Membre confirmé

    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    137
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 137
    Par défaut
    Bon eh bien voilà un code qui fonctionne à tous les coups (pour l'instant plus de problème ) ... Un grand merci à yarp pour m'avoir mis sur la voie.

    Je pense que ce code mérite de figurer dans la FAQ ou dans les sources. (j'ai cherché pendant plusieurs jours cette solution).

    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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    bool __fastcall DrawBitmapToPrinterCanvas(Graphics::TBitmap *pBitmap, TCanvas *pCanvas, TRect rect)
    {
        int w = pBitmap->Width;
        int h = pBitmap->Height;
        HDC h_dc = pBitmap->Canvas->Handle;
        HDC h_mem_dc = ::CreateCompatibleDC(h_dc);
        HBITMAP h_mem_bmp = ::CreateCompatibleBitmap(h_dc, w, h);
        HBITMAP h_old_bmp = ::SelectObject(h_mem_dc, h_mem_bmp);
        int result = GDI_ERROR;
     
        //copy the image on the memory dc
        ::BitBlt(h_mem_dc, 0, 0, w, h, h_dc, 0, 0, SRCCOPY);
     
        //delete the mem dc
        ::SelectObject(h_mem_dc, h_old_bmp);
        ::DeleteDC(h_mem_dc);
     
        //get memory for a BITMAPIFO structure
        HANDLE h_bmp_info = ::GlobalAlloc(GHND, sizeof(BITMAPINFO) + (sizeof(RGBQUAD) * 256));
        BITMAPINFO* bmp_info = static_cast<BITMAPINFO*> (::GlobalLock(h_bmp_info));
        //set up the structure
        memset(bmp_info, NULL, sizeof(BITMAPINFO) + (sizeof(RGBQUAD) * 255));
        bmp_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bmp_info->bmiHeader.biPlanes = 1;
        bmp_info->bmiHeader.biBitCount = 16; //ou 32
        bmp_info->bmiHeader.biWidth = w;
        bmp_info->bmiHeader.biHeight = h;
        bmp_info->bmiHeader.biCompression = BI_RGB;
     
        //find out how much memory for the bits
        if (::GetDIBits(h_dc, h_mem_bmp, 0, h, NULL, bmp_info, DIB_RGB_COLORS))
            {
            //Allocate memory for the bits
            HANDLE h_bits = ::GlobalAlloc(GHND, bmp_info->bmiHeader.biSizeImage);
            void *bits = ::GlobalLock(h_bits);
            //this time get the bits
            if (::GetDIBits(h_dc, h_mem_bmp, 0, h, bits, bmp_info, DIB_RGB_COLORS) != 0)
                {
                //send the bits to the printer
                result = StretchDIBits(pCanvas->Handle, rect.Left, rect.Top, rect.Width(), rect.Height(),
                              0, 0, w, h,
                              bits, bmp_info,
                              DIB_RGB_COLORS, SRCCOPY);
                }
            ::GlobalUnlock(bits);
            ::GlobalFree(h_bits);
            }
        //clean up
        ::DeleteObject(h_mem_bmp);
        ::GlobalUnlock(bmp_info);
        ::GlobalFree(h_bmp_info);
        return (result != GDI_ERROR);
    }

  8. #8
    Membre expérimenté

    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    288
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Janvier 2003
    Messages : 288
    Par défaut
    Bravo, et désolé de ne pas avoir donnée suite, pas mal de boulot et de soucis moi aussi.

    Par contre je suis surpris que GlobalAlloc fonctionne mieux que HeapAlloc (j'avais utilisé GlobalAlloc dans une version intermédiaire puis je suis passé à HeapAlloc).

    Ce qui est sur, c'est que ce code mérite de figuer dans la FAQ.

    yarp
    www.senosoft.com

  9. #9
    Membre expérimenté

    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    288
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Janvier 2003
    Messages : 288
    Par défaut
    Je me suis penché sur le problème HeapAlloc/GlobalAlloc aujourd'hui. La doc du MSDN est claire à ce sujet.

    "Les fonctions globales sont plus lentes que les autres fonctions de gestion de la mémoire et n'ont pas autant de possibilités. Par conséquent, les nouvelles applications doivent utiliser les fonctions Heap. cependant, les fonctions Globales sont encore utilisées par DDE et le clipboard".

    J'en déduit que pour une imprimante, ou pour certaines imprimantes, le protocole de communication devait être similaire à DDE/clipboard et ne supportait que GlobalAlloc.

    Moralité: Le Msdn dit que ça marche mieux avec HeapAlloc sauf quand ça ne marche pas. Mais ne nous dit pas (du moins pas ici) comment reconnaitre les périphériques (cartes vidéos, imprimantes) qui ne supportent pas HeapAlloc.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. VC++ Direct3D8, problème avec LPD3DXFONT et LPD3DTEXTURE8
    Par Magus (Dave) dans le forum DirectX
    Réponses: 3
    Dernier message: 03/08/2002, 11h10
  2. Problème avec [b]struct[/b]
    Par Bouziane Abderraouf dans le forum CORBA
    Réponses: 2
    Dernier message: 17/07/2002, 10h25
  3. Problème avec le type 'Corba::Any_out'
    Par Steven dans le forum CORBA
    Réponses: 2
    Dernier message: 14/07/2002, 18h48
  4. Problème avec la mémoire virtuelle
    Par Anonymous dans le forum CORBA
    Réponses: 13
    Dernier message: 16/04/2002, 16h10

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