Getdibits qui ne marche pas

Signaler
Messages postés
220
Date d'inscription
lundi 30 octobre 2000
Statut
Membre
Dernière intervention
15 juillet 2013
-
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
-
Voilà, j'ai un souci avec la fonction GetDibits.
Je veux récupérer les pixels de l'écran complet.
A la fin de la procedure FormCreate, j'utilise GetDiBits.
Mais ça marche pas...
le pointeur tmp passe de la zone pointée à la valeur $FF. (pas terrible comme adresse mémoire)
et le code d'erreur de retour et 6 (invalide handle).
J'ai le souci dans plein de cas, comme  pour l'effet d'eau que j'avais posté sur ce site.
J'ai beau retourner et recompiler dans tout les sens... ça ne marche pas...

HELP ME...



// alloue de l'espace mémoire
function WinGetMem(Size:Cardinal):pointer;
begin
  result:=VirtualAlloc(nil,Size,MEM_COMMIT or MEM_RESERVE,PAGE_READWRITE);
  if not Assigned(result) then  RaiseLastOSError;
end;



//libère la mémoire
procedure WinFreeMem(p:Pointer);
begin
  if Assigned(p) and not VirtualFree(p,0,MEM_RELEASE) then RaiseLastOSError;
end;



procedure TForm1.FormCreate(Sender: TObject);
begin
 //précalcul les coefficients de la lentille
 PreCalcul;
 // crée l'image de fond (capture de l'écran)
 fond:=tbitmap.Create;
 fond.Width:=screen.Width;
 fond.Height:=screen.Height;
 fond.PixelFormat:=pf32bit;
 // copie l'image de l'écran comme il est maintenant
 BitBlt(fond.Canvas.Handle,0,0,fond.Width,fond.Height,getdc(0),0,0,cmSrcCopy);



 // prépare la structure bi (bitmap info)
 fillchar(bi,sizeof(bi.bmiHeader),0);
 bi.bmiHeader.biSize:=sizeof(bi.bmiHeader);
 // appel de getdibits avec nil comme pointeur pour avoir juste les infos dans bi
 getdibits(fond.Canvas.Handle,fond.Handle,0,fond.height,nil,bi,DIB_RGB_COLORS);
 // alloue la mémoire
 buffer:=wingetmem(bi.bmiHeader.biSizeImage);
 tmp:=wingetmem(bi.bmiHeader.biSizeImage);



 // prépare la structure bi (bitmap info)
 fillchar(bi,sizeof(bi.bmiHeader),0);
 bi.bmiHeader.biSize:=sizeof(bi.bmiHeader);
 // appel de getdibits avec tmp pour récupérer les pixels de l'image fond
 getdibits(fond.Canvas.Handle,fond.Handle,0,fond.height,tmp,bi,DIB_RGB_COLORS);
 caption:=inttostr(GetLastError);
end;

9 réponses

Messages postés
1023
Date d'inscription
dimanche 1 août 2004
Statut
Membre
Dernière intervention
17 août 2008
2
Salut !

GetDIBits ne fonctionne qu'avec un bitmap ! Pas avec un simple contexte de périphérique (DC pur Device Contect).
Tu dois donc créer un bitmap et faire ton BitBlt dedans.

Sinon, pourquoi récupérer le bitmapinfo à l'aide d'un premier appel à GetDIBits pour finalement écraser les valeurs ?

Et, autre chose: on a eu une conversation avec Cirec hier et, d'après moi, GetDIBits/SetDIBits ne serait pas plus rapide qu'un "bon" Scanline[]. Chez moi, pour une opération toute simple, SetDIBits me prend le double du temps que met Scanline[].

Dernière chose: tu fais GetDc(0) dans le paramètre de BitBlt mais tu dois récupérer la valeur fournie par la fonction pour pouvoir libérer le DC que tu viens de réserver. Me trompe-je ? (ch'uis pas 100% sûr sur ce coup)
Il me semble pourtant que cela devrait être cela :
 dc0 := GetDC(0);
 BitBlt(fond.Canvas.Handle,0,0,fond.Width,fond.Height,getdc(0),0,0,cmSrcCopy);
 ReleaseDC(dc0);

Alors, toujours des problèmes ?
Messages postés
1023
Date d'inscription
dimanche 1 août 2004
Statut
Membre
Dernière intervention
17 août 2008
2
Bon, dans mon code, j'ai oublié de remplacer le getdc(0) (vive le copier-coller ^^).

Sinon, j'ai vu que tu a écrit cela sur une source de mauricio:

<hr size="2" width="100%" />Pour le test de performance, j'ai deux réponses :
Dans ma source Flames version 2, j'ai remplacé les scanline par ce système, on double en gros la fréquence d'affichage.
La réponse est simple, à chaque demande d'un pointeur par scanline, dernière, il y a ceci :

function TBitmap.GetScanLine(Row: Integer): Pointer;
begin
  Changing(Self);
  with FImage.FDIB, dsbm, dsbmih do
  begin
    if (Row < 0) or (Row >= bmHeight) then
      InvalidOperation(@SScanLine);
    DIBNeeded;
    GDIFlush;
    if biHeight > 0 then  // bottom-up DIB
      Row := biHeight - Row - 1;
    Integer(Result) := Integer(bmBits) +
      Row * BytesPerScanline(biWidth, biBitCount, 32);
  end;
<hr size="2" width="100%" />
Cela est bien évidemment juste. Cela dit, tu peux ne faire que deux appels à Scanline[] dans tout ton traitement et incrémenter le pointeur de données. Dans ce cas là, le problème ne se pose plus.
Mais le gain de performance n'est pas énorme: 1ms à peine pour une image de 1024 lignes !!! C'est dire à quel point cette procédure est rapide !

Par contre, ce qui est lent, c'est de devoir copier TOUS les pixels lors d'une affectation par SetDIBits. Du coup, c'est moins performant. (avec Scanline on travaille directement sur le bloc concerné) Et en plus, tu dois utiliser deux fois plus de mémoire !

Bref, j'attends quand même ta réponse, le débat risque d'être intéressant !

++
Messages postés
220
Date d'inscription
lundi 30 octobre 2000
Statut
Membre
Dernière intervention
15 juillet 2013

alors, en deux mot, j'appelle une première fois getdibits pour remplir bitmapinfo et ainsi avoir la taille de l'image complète.
Normalement, le deuxième ne sers à rien, mais si je l'enlève, ça ne marche plus, c'est dans ce cas d'ailleurs que le pointeur pert sa valeur.

Bon, sinon, je viens de regarder de plus près tout ça...
Et il me semble en effet que les deux soit pareil.
Pour la petite histoire, j'avait besoin de visualiser rapidement un tableau de byte sous forme de bitmap.
En regardant à gauche à droite sur le web, j'ai trouvé ça, Getdibits et Setdibits.
Dans mon cas, j'avais un pointeur et je ne voulais pas faire une fonction qui recopie le tableau byte à byte vers un tbitmap.
Et en utilisant une scanline pour avoir le pointeur vers la première ligne puis en recopiant tout d'un bloc, il y a une magnifique erreur car un écris n'importe où  à la fin de la ligne...
Je me suis donc dis que scanline donne un pointeur vers une ligne mais pas à l'ensemble du bitmap (tel la mémoire vidéo).
En fait, je viens de voir que je me suis gouré... mais il faud demander un scanline sur la dernière ligne puis le pointeur donne tout le bitmap mais (comme tout les bitmaps) de du bas vers le haut...
Et Et au fin fond des choses, ça revient au même que SetDibits... qui fait en gros ça :

p,tableau : pointer;

p:=bitmap.scanline[bitmap.heigth-1];
move(tableau,p,taille_du_bitmap)

donc plus besoin de setdibits... sniff...

Cependant j'aimerai bien savoir pourquoi ça ne marche pas.
Messages postés
702
Date d'inscription
vendredi 21 mars 2003
Statut
Membre
Dernière intervention
1 octobre 2009
4
"En fait, je viens de voir que je me suis gouré... mais il faud demander
un scanline sur la dernière ligne puis le pointeur donne tout le bitmap
mais (comme tout les bitmaps) de du bas vers le haut..."

Salut barbichette,

Il faut quand même faire gaffe avec Scanline ! Les lignes du bitmap sont alignées. Je sais c'est pas très clair, je m'explique 
:

Les pointeurs renvoyés par Scanline sur chaque ligne sont tous divisibles par 8 (alignement sur un DWord).
Si (Nombre de pixels par ligne) x (Nombre d'octet par pixel) n'est pas multiple de 8, il y aura quelques octets non significatifs entre deux lignes. Pas très pratique !

Je dit 8 mais c'est peut-être 4

Ken@vo

<hr size="2" width="100%" />Code, Code, Codec !

[%3C/body ]
Messages postés
1023
Date d'inscription
dimanche 1 août 2004
Statut
Membre
Dernière intervention
17 août 2008
2
"Normalement, le deuxième ne sers à rien"
=> non, c'est le premier appel à GetDIBits qui ne sert à rien (dans ton cas) puisque tu vides la structure qu'il te remplit !
Le deuxième est forcément nécessaire, c'est lui qui copie les données.

"J'avais besoin de visualiser rapidement un tableau de byte sous forme de bitmap"
=> Deux solutions: SetDIBits ou Scanline[]. Mais ça, tu l'as bien compris.

"Je me suis donc dis que scanline donne un pointeur vers une ligne mais pas à l'ensemble du bitmap"
=> C'est juste ! Pour avoir une autre ligne, tu refais un appel à Scanline[] ou tu incrémente ton pointeur de la taille de la ligne (en respectant les alignements, voir plus bas)

"Cependant j'aimerai bien savoir pourquoi ça ne marche pas."
=> En fait, les données des bitmaps sont alignées sur des double-mots (DWord ou Cardinal). Ce qui veut dire qu'a la fin d'une ligne, il faut rajouter les zéros jusqu'à tomber sur une limite d'un mot. Dans le cas ou le format est pf32Bit, il n'y a aucun problème puisque chaque pixel se termine sur la fin d'un mot, la fin de la ligne aussi (logique).

Par contre, en pf24Bit, suivant la largeur de ton bitmap, ce n'est pas le cas. Exemple avec un bitmap de 3 pixels de largeur, les données de couleur sont stockées comme ça (une lettre: un octet - les apostrophes indiquent les fins de double-mots):

BGRB'GRBG'R000'
BGRB'GRBG'R000'

Tu vois donc bien qu'on a rajouté trois zéro pour compléter la ligne.
De plus, pour finir, les bitmaps ne sont pas toujours orientés de bas en haut.

Pour finir, je te laisse voir ici cette merveilleuse traduction de nono40 sur [http://nono40.developpez.com/tutoriel/delphi/efg/scanline/#LV l'utilisation de Scanline[]]

A+
Messages postés
1023
Date d'inscription
dimanche 1 août 2004
Statut
Membre
Dernière intervention
17 août 2008
2
@Kenavo: ahh, croisement de messages ! Au moins, je pense que c'est clair maintenant !
Messages postés
1023
Date d'inscription
dimanche 1 août 2004
Statut
Membre
Dernière intervention
17 août 2008
2
Ahhh, je viens de passer une demi-heure sur un problème de GetDIBits !!!
En fait, les données de GetDIBits sont aussi alignées sur des double-mots, ce qui fait qu'au final, c'est plus compliqué ! (en fait, c'est aussi compliqué à gérer qu'avec Scanline[]).

Alors forcément le problème ne se pose pas lorsque les dimensions font en sorte que (W * 3) / 4 est un nombre entier et pareil pour (H * 3) / 4 mais partout ailleurs, c'est le bug assuré (et bonne chance pour comprendre, le message d'erreur obtenu est "descripteur non valide" !)

Conclusion: GetDIBits/SetDIBits n'a aucun intérêt et n'est pas du tout pratique !!!

Oubliez le !!! Préférez lui un bon Scanline[]

++
Messages postés
220
Date d'inscription
lundi 30 octobre 2000
Statut
Membre
Dernière intervention
15 juillet 2013

ce que je ne comprend pas, c'est la chose suivante.
(on oublie le code de mon premier post)
J'appel une première fois GetDibits avec nil comme pointer de bits.
sur la MSDN, ils disent que ça rempli la structure bitmapinfo.
Et en particulier le membre biSizeImage qui donne la taille du buffer pour recevoir les bits.
Puis on alloue de l'espace memoire en utilisant cette taille.
Et je rappel getdibits pour transferer l'image.
Mais voilà, une fois sur deux, il y a un débordement je ne sais pas trop où et soit le pointer reçois la valeur $FF, soit il y a erreur à l'execution, soit une autre variable globale de mon programme vois sa valeur modifier... C'est du n'importe quoi....

Par ailleurs, je viens de comprendre un truc important avec getdibits et setdibits.
Lorsque le membre bmiColors est renseigné dans la structure bitmapinfo, la copie se fait en cherchant la meilleur concordance entre les pixels du buffer et la palette bmiColors, et la palette du bitmap où l'on copie.
Donc, diminuer le nombre de couleur ou appliquer une autre palette à une image en ne perdant que peu l'image elle même.
Ca peut êter utilile pour faire rapidement ce genre de processus sans avoir à coder toute une fonction.
encore faut il que ça marche...

Barbichette
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
15
Salut,

Bin éventuellement, montre le code, vérifie la valeur retournée par GetDIBits (non nulle pour l'appel avec lpvBits, la taille des données quand le buffer n'est pas à nil...). Assure toi que les handles de ta bitmap et de ton hDC sont valide. D'ailleur je me demande quel handle de DC ils veulent... (Windows permet de créer un DC compatible avec une bitmap à l'aide de CreateCompatibleDC, et on peut utiliser SelectObject pour spécifier la bitmap d'un DC)

Et surtout, utilise scanline !