begin process at 2008 05 16 04:33:34
1 173 215 membres
57 nouveaux aujourd'hui
13 970 membres club

Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

Sujet : Getdibits qui ne marche pas [ Système / Mémoire ] (barbichette)

Getdibits qui ne marche pas le 20/06/2007 19:42:40

barbichette
Membre Club

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;


Re : Getdibits qui ne marche pas le 20/06/2007 20:10:44

florenth
Membre Club
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 ?


Re : Getdibits qui ne marche pas le 20/06/2007 20:23:11

florenth
Membre Club
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:



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;


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 !

++

Re : Getdibits qui ne marche pas le 20/06/2007 22:21:46

barbichette
Membre Club
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.

Re : Getdibits qui ne marche pas le 21/06/2007 10:05:21

Kenavo
"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

Code, Code, Codec !


Re : Getdibits qui ne marche pas le 21/06/2007 10:33:37

florenth
Membre Club
"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 l'utilisation de Scanline[]

A+



Re : Getdibits qui ne marche pas le 21/06/2007 10:51:17

florenth
Membre Club
@Kenavo: ahh, croisement de messages ! Au moins, je pense que c'est clair maintenant !

Re : Getdibits qui ne marche pas le 21/06/2007 21:46:02

florenth
Membre Club
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[]

++

Re : Getdibits qui ne marche pas le 22/06/2007 11:51:02

barbichette
Membre Club
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

Re : Getdibits qui ne marche pas le 22/06/2007 14:05:25

rt15
Membre Club
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 !




Classé sous : fond, handle, bi, bmiheader, getdibits

Participer à cet échange

Appels d'offres

Pub



CalendriCode

Mai 2008
LMMJVSD
   1234
567891011
12131415161718
19202122232425
262728293031 

Téléchargements

Logiciels à télécharger sur le même thème :

Boutique

Boutique de goodies CodeS-SourceS