florenth
-
10 mai 2007 à 19:15
cincap
Messages postés460Date d'inscriptiondimanche 5 décembre 2004StatutMembreDernière intervention 6 avril 2009
-
1 oct. 2007 à 13:18
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.
cincap
Messages postés460Date d'inscriptiondimanche 5 décembre 2004StatutMembreDernière intervention 6 avril 20092 1 oct. 2007 à 13:18
Pour Barbichette, c'est ok pour moi, j'ai vu qu'il fallait mettre en place le fichier dans Windows pour avoir accès aux paramètres.
A ce sujet, si après un aperçu je décide d'annuler j'obtiens une exception et obligé de fermer le processus.
@+,
Cincap
cincap
Messages postés460Date d'inscriptiondimanche 5 décembre 2004StatutMembreDernière intervention 6 avril 20092 1 oct. 2007 à 13:01
Bonjour à toutes et à tous,
@ Cirec, avec Delphi 6 et tes modifications telles quelles, j'ai une erreur de compilation.
@ Barbichette, j'ai jeté un coup d'oeil à ton effet de flamme, c'est super et par hasard, y a t'il moyen de modifier la couleur (flamme).
@+,
Cincap
cs_jpt
Messages postés7Date d'inscriptionsamedi 12 octobre 2002StatutMembreDernière intervention25 août 2007 25 août 2007 à 16:38
Bonjour à tous,
(j'espère que ce post 3 mois plus tard ne sera pas complètement perdu, :-) )
J'ai utilisé les modifs de cirec (post du 12/05 à 22:31) et ai gagné au concours des AV, grace à la ligne :
GetDIBits(BMP.Canvas.Handle, BMP.Handle, 0, BmpSrcHeight,
PImgArrayPix, ImgBitInfo, DIB_RGB_COLORS);
Je m'en suis sorti avec l'amie Esperluette, comme ça :
GetDIBits(BMP.Canvas.Handle, BMP.Handle, 0, BmpSrcHeight,
@PImgArrayPix, ImgBitInfo, DIB_RGB_COLORS);
Cependant, ça n'est pas pour autant que je récupère l'effet original au clic sur l'image : chez moi (D7 perso sous w2k), l'image devient toute noire et vire au rouge de clic en clic.
Doit y avoir un 'tit 'blème car je peux distinguer tout en haut de l'image une ligne de couleurs diverses et variées...
Et si j'utilise encore mon amie Esperluette :
SetDIBits(ImgResult.Canvas.Handle, ImgResult.Picture.Bitmap.Handle,
0, BmpSrcHeight, @PImgArrayPix, ImgBitInfo, DIB_RGB_COLORS);
alors là, il ne se passe rien aux clics... :-(
Protocole de manip : downloadé la source et joué un peu avec, puis copié/collé le code de cirec et c'est tout.
Hélas, mes compétences graphiques atteignent vite leurs limites sur ce coup-là.
@ Barbichette:
Pour l'exemple des flammes je veux bien c'est un cas particulier (l'image et la palette sont crées de toutes pieces) mais pour les autres exemples (les reflets et celle-ci) les images sont connues à l'avance et on peut donc utiliser directement un PByte ce qui rend le code plus simple et plus rapide.
Comme le but est quand même d'utiliser une procedure ultra-rapide il n'est peut être pas utile de la ralentir et/ou de la compliquer, enfin c'est mon avis ...
@ Forman:
En fait, je ne doute pas que ce genre de problème puisse survenir mais en lisant
ton message sur la source de Barbichette j'avais compris que le problème survenait automatiquement
avec des images de tailles supérieurs à 800*600 d'ou mon teste en 2304*1024.
Conclusion il vaut donc mieux utiliser tes deux méthodes
WinGetMem et WinFreeMem
pour un maximum de sécurité
@+
Cirec
cs_Forman
Messages postés600Date d'inscriptionsamedi 8 juin 2002StatutMembreDernière intervention 6 avril 20101 14 mai 2007 à 17:27
@ Cirec:
tu dis que tu n'as pas eu de problèmes mais je t'assure que ça arrive. Ca dépend déjà de la taille des pages allouées par défaut par le système. De plus, si ton programme utilise peu de resources (en dehors de celles utilisées par le bitmap) le problème ne se produit pas car les GetMem (implicites ou non, liés à l'allocation des descendants de TObject par exemple) ne remplissent pas une page complète et le memory manager de base de Delphi commence par allouer au début d'une page, donc le problème n'est pas visible. Mais si tu fais de nombreuses allocations, le problème apparaîtra sûrement... je l'ai vérifié expérimentalement. Ca ne coûte de toute façon pas très cher de faire un WinGetMem avec ma fonction plutôt qu'un GetMem standard, et en terme de performance c'est de toute façon meilleur car le memory manager de Delphi est plutôt adapté à des allocations de taille réduite (exemple: la méthode implicite NewInstance de la classe TObject) en terme de vitesse, et dans le cas de larges zones de mémoires (typiquement des images donc), le memory manager de Windows est meilleur.
Attention je n'utilise pas un PByteArray mais un PByte ...
un simple pointeur sur Byte : BPyte = ^Byte;
Et le calcule se fait simplement :
GetMem(PByte, Bmp.Width * Bmp.Height * BitPerPixel)
Ce qui ta induit en erreur c'est que j'ai conservé le nom que Mauricio avait donné (PImgArrayPix)
L'astuce réside dans l'utilisation d'un entier pour lire soit un Quad soit un Triple
var
FPosition,
clData,
BytesTotal: integer;
Pix : TRGBQuad ABSOLUTE clData;
Begin
...
Move(Pointer(Longint(PImgArrayPix) + FPosition)^, clData, BytesPerPixel);
L'affectation se fait automatiquement par Delphi dans Pix et en fonction de BytesPerPixel
rgbReserved est ignoré ou non (très pratique)
Ton calcule d'espace mémoire ma parait un peut hazardeux ...
sizeof(bitinfo.bmiHeader) = 40
sizeof(bitinfo.bmiColors) = 4
getmem(pinfo,sizeof(bitinfo.bmiHeader)+256*sizeof(bitinfo.bmiColors)) = 1184
Les données sont des Bytes,
pf24Bit = 3 BytesPerPixel,
pf32Bit = 4 BytesPerPixel,
Les données(sur disque et en mémoire) sont des Bytes, Des séries de 3 ou 4 Bytes (en fonction de BytesPerPixel)
Le total des pixels d'un Bmp = Width * Height
Pour connaitre la taille des données : Total des pixels * BytesPerPixel
Et cette taille peut être verifiée puisqu'elle correspond a la taille du fichier sur le disque moins TBITMAPFILEHEADER.bfOffBits
Voilà .. rien de plus simple
Pour la mémoire je ferai encore d'autres testes
@+
Cirec
cs_MAURICIO
Messages postés2106Date d'inscriptionmardi 10 décembre 2002StatutModérateurDernière intervention15 décembre 20145 14 mai 2007 à 13:09
Merci Barbichette d' expliquer à quoi sert ce machin truc bizarre lol...
Le but étant tout simplement de montrer comment manipuler les pixels d' un Bitmap en super rapide ...
Je tiendrai compte des commentaires et je mettrai une nouvelle version quand je pourrai.
A+
cs_barbichette
Messages postés220Date d'inscriptionlundi 30 octobre 2000StatutMembreDernière intervention15 juillet 2013 14 mai 2007 à 12:46
en fait,
ces fonctions marche en général mais si tu te retrouve avec une mémoire alloué sur plusieurs pages non contigues, si qui peutce produire avec getmem, les fonctions ne marcherons plus.
J'ai en effet eu le souci sans savoir d'où ça venait.
Maitenant, suivant la machine, et la mémoire dispo, ça peut marcher...
Enfin, je rajouterai que pour des petites images, la fonction GetBitmapBits marche très bien et elle est plus simple.
GetBitmapBits(hbitmap,count,pointeur)
count=nombre d'octets à copier
cs_barbichette
Messages postés220Date d'inscriptionlundi 30 octobre 2000StatutMembreDernière intervention15 juillet 2013 14 mai 2007 à 12:40
Salut Cirec,
en fait, c'est pas moi qui invente ce bidouillage.
Dans l'unité Windows, tu trouve :
tagBITMAPINFO = packed record
bmiHeader: TBitmapInfoHeader;
bmiColors: array[0..0] of TRGBQuad;
end;
C'est une astuce un peut particulière pour préciser que le tableau bmicolors est bien juste après le reste en mémoire, mais qu'on ne connait pas la taille au moment de l'écriture du code.
Il faut donc réservé de la mémoire avec getmem par exemple puis lire le tableau en dehors des indices finalement.
Dans ce cas, pour avoir un talbeau bmicolors de 256 TRGBQuad il faut donc faire
var pinfo:PBitmapInfo;
getmem(pinfo,sizeof(bitinfo.bmiHeader)+256*sizeof(bitinfo.bmiColors))
C'est pas très simple, mais ça marche bien.
En regardant le code en assembleur produit, un array[0..0] et un pbytearray utilise toute les deux qu'une seule instruction. La différence vient de la zone mémoire utilisée.
et si tu utilises PByte à la place de Array[0..0] tu divises le temps de traitement des données par 3
Et en plus le code devient plus simple.... Plus de tableau, le tout se fait avec un pointeur unique.
D'ailleurs d'ou sort tu ce bidouillage/bricolage (Array[0..0])?
HFR11:
Ce code permet un accès direct aux données(tous les pixels) d'un Bitmap par l'intermédiaire d'un pointeur unique.
Et si tu as du mal avec ce code c'est qu'il faudrait voir/revoir les bases sur les pointeurs.
@+
Cirec
cs_barbichette
Messages postés220Date d'inscriptionlundi 30 octobre 2000StatutMembreDernière intervention15 juillet 2013 14 mai 2007 à 11:50
bonjour,
En gros, le code ouvre une image bitmap, et recopie tout les octets de l'image dans un tableau.
A chaque clique sur l'image, la composante rouge est augmentée et les composantes bleu et vert sont diminuées dans le tableau.
Puis les octets du tableau sont transferés directement dans le Bitmap pour un affichage.
Ce code, comme le dit le titre, donne un exemple simple d'utilisation de GetDiBits et SetDiBits.
Barbichette
hfr11
Messages postés20Date d'inscriptionmardi 17 décembre 2002StatutMembreDernière intervention 8 octobre 2019 14 mai 2007 à 08:16
Bonjour,
Tout ça a l'air très sympathique... Mais pensons aux débutants, aux curieux et à ceux qui veulent s'enrichier sans être obligés pour chaque code paru de déchiffrer l'algorythme. A quoi sert ce code ? Que fait ce code ?
Merci...
Bon courage et bonne bourre, cordialement, Patrice
cs_barbichette
Messages postés220Date d'inscriptionlundi 30 octobre 2000StatutMembreDernière intervention15 juillet 2013 14 mai 2007 à 01:52
Salut,
Pour le test de performance, j'ai deux réponses :
Dans ma source Flames version 2, j'ai remplacé les scanline par ce systeme, 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 :
=unit Graphics====
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;
end;
==================
J'ai par ailleurs créé un programme que je n'ai pas mis ici qui trace des ellipses avec l'algorithme de Bresenham.
j'affiche une série de 1400 cercles deux fois plus vite.
Une autre raison à celà :
je dessine les 1400 cercles en mémoire et je met à jour le tout en une seule fois
Par contre, avec des scanline[] et des pixel[], l'image est mise à jour à chaque changement de pixel.
je pense que même si l'utilisation de Get/SetDiBits n'est pas courrante
ça reste quand même à un niveau débutant !
Au niveau du code:
!!!!!! << ImgResult.Picture.Bitmap := TBitmap.Create; >> !!!!!!
Cette ligne est source de fuite de mémoire ... en effet le bitmap ainsi crée
n'est jamais liberé.
Et array[0..0] c'est pas top comme bidouille sans compter que ça complique les choses ...
Donc voici un code modifié:
1: il charge le Bitmap depuis le fichier
(pour l'exemple j'ai enregistrer ton image dans le répertoire du projet sous "Img.bmp"
avec l'inspecteur d'objets)
2: ImgBitInfo est chargé depuis le fichier BMP
3: Utilisation de PByte à la place de Array[0..0]. Le code devient beaucoup plus simple
et plus rapide (la lecture et l'écriture ne se fait plus byte à byte mais par bloc
de 3 ou 4 bytes en fonction du PixelFormat(pf24Bit ou pf32Bit) du BMP chargé).
4: Après avoir enregistrer le BMP sur le disque et apporté les modifications on peut
supprimer ImgSrc
Le Code ...
type
TForm1 = class(TForm)
ImgResult: TImage;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure ImgResultMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
// Suppression de Array[0..0] Of byte
// au profit de PByte ce qui allège le code
// TArrayInt = array[0..0] of byte;
// PArrayInt = ^TArrayInt;
var
Form1: TForm1;
ImgBitInfo: TBitmapInfo; // structure pour getdibits et setdibits
PImgArrayPix: PByte;//PArrayInt; // buffer de l'image
Très très bon code! Je n'avais moi non plus, pas réussi à utiliser le code de Barbichette.
Cependant, as-tu fait des mesures de performances avec ScanLine[]. Si oui, est-ce vraiment plus rapide ou bien le gain apporté n'est que mineur ?
1 oct. 2007 à 13:18
A ce sujet, si après un aperçu je décide d'annuler j'obtiens une exception et obligé de fermer le processus.
@+,
Cincap
1 oct. 2007 à 13:01
@ Cirec, avec Delphi 6 et tes modifications telles quelles, j'ai une erreur de compilation.
@ Barbichette, j'ai jeté un coup d'oeil à ton effet de flamme, c'est super et par hasard, y a t'il moyen de modifier la couleur (flamme).
@+,
Cincap
25 août 2007 à 16:38
(j'espère que ce post 3 mois plus tard ne sera pas complètement perdu, :-) )
J'ai utilisé les modifs de cirec (post du 12/05 à 22:31) et ai gagné au concours des AV, grace à la ligne :
GetDIBits(BMP.Canvas.Handle, BMP.Handle, 0, BmpSrcHeight,
PImgArrayPix, ImgBitInfo, DIB_RGB_COLORS);
Je m'en suis sorti avec l'amie Esperluette, comme ça :
GetDIBits(BMP.Canvas.Handle, BMP.Handle, 0, BmpSrcHeight,
@PImgArrayPix, ImgBitInfo, DIB_RGB_COLORS);
Cependant, ça n'est pas pour autant que je récupère l'effet original au clic sur l'image : chez moi (D7 perso sous w2k), l'image devient toute noire et vire au rouge de clic en clic.
Doit y avoir un 'tit 'blème car je peux distinguer tout en haut de l'image une ligne de couleurs diverses et variées...
Et si j'utilise encore mon amie Esperluette :
SetDIBits(ImgResult.Canvas.Handle, ImgResult.Picture.Bitmap.Handle,
0, BmpSrcHeight, @PImgArrayPix, ImgBitInfo, DIB_RGB_COLORS);
alors là, il ne se passe rien aux clics... :-(
Protocole de manip : downloadé la source et joué un peu avec, puis copié/collé le code de cirec et c'est tout.
Hélas, mes compétences graphiques atteignent vite leurs limites sur ce coup-là.
Valà, c'étaient mes 2 cts,
19 mai 2007 à 13:49
Pour l'exemple des flammes je veux bien c'est un cas particulier (l'image et la palette sont crées de toutes pieces) mais pour les autres exemples (les reflets et celle-ci) les images sont connues à l'avance et on peut donc utiliser directement un PByte ce qui rend le code plus simple et plus rapide.
Comme le but est quand même d'utiliser une procedure ultra-rapide il n'est peut être pas utile de la ralentir et/ou de la compliquer, enfin c'est mon avis ...
@ Forman:
En fait, je ne doute pas que ce genre de problème puisse survenir mais en lisant
ton message sur la source de Barbichette j'avais compris que le problème survenait automatiquement
avec des images de tailles supérieurs à 800*600 d'ou mon teste en 2304*1024.
Conclusion il vaut donc mieux utiliser tes deux méthodes
WinGetMem et WinFreeMem
pour un maximum de sécurité
@+
Cirec
14 mai 2007 à 17:27
tu dis que tu n'as pas eu de problèmes mais je t'assure que ça arrive. Ca dépend déjà de la taille des pages allouées par défaut par le système. De plus, si ton programme utilise peu de resources (en dehors de celles utilisées par le bitmap) le problème ne se produit pas car les GetMem (implicites ou non, liés à l'allocation des descendants de TObject par exemple) ne remplissent pas une page complète et le memory manager de base de Delphi commence par allouer au début d'une page, donc le problème n'est pas visible. Mais si tu fais de nombreuses allocations, le problème apparaîtra sûrement... je l'ai vérifié expérimentalement. Ca ne coûte de toute façon pas très cher de faire un WinGetMem avec ma fonction plutôt qu'un GetMem standard, et en terme de performance c'est de toute façon meilleur car le memory manager de Delphi est plutôt adapté à des allocations de taille réduite (exemple: la méthode implicite NewInstance de la classe TObject) en terme de vitesse, et dans le cas de larges zones de mémoires (typiquement des images donc), le memory manager de Windows est meilleur.
14 mai 2007 à 14:05
un simple pointeur sur Byte : BPyte = ^Byte;
Et le calcule se fait simplement :
GetMem(PByte, Bmp.Width * Bmp.Height * BitPerPixel)
Ce qui ta induit en erreur c'est que j'ai conservé le nom que Mauricio avait donné (PImgArrayPix)
L'astuce réside dans l'utilisation d'un entier pour lire soit un Quad soit un Triple
var
FPosition,
clData,
BytesTotal: integer;
Pix : TRGBQuad ABSOLUTE clData;
Begin
...
Move(Pointer(Longint(PImgArrayPix) + FPosition)^, clData, BytesPerPixel);
L'affectation se fait automatiquement par Delphi dans Pix et en fonction de BytesPerPixel
rgbReserved est ignoré ou non (très pratique)
Ton calcule d'espace mémoire ma parait un peut hazardeux ...
sizeof(bitinfo.bmiHeader) = 40
sizeof(bitinfo.bmiColors) = 4
getmem(pinfo,sizeof(bitinfo.bmiHeader)+256*sizeof(bitinfo.bmiColors)) = 1184
Les données sont des Bytes,
pf24Bit = 3 BytesPerPixel,
pf32Bit = 4 BytesPerPixel,
Les données(sur disque et en mémoire) sont des Bytes, Des séries de 3 ou 4 Bytes (en fonction de BytesPerPixel)
Le total des pixels d'un Bmp = Width * Height
Pour connaitre la taille des données : Total des pixels * BytesPerPixel
Et cette taille peut être verifiée puisqu'elle correspond a la taille du fichier sur le disque moins TBITMAPFILEHEADER.bfOffBits
Voilà .. rien de plus simple
Pour la mémoire je ferai encore d'autres testes
@+
Cirec
14 mai 2007 à 13:09
Le but étant tout simplement de montrer comment manipuler les pixels d' un Bitmap en super rapide ...
Je tiendrai compte des commentaires et je mettrai une nouvelle version quand je pourrai.
A+
14 mai 2007 à 12:46
ces fonctions marche en général mais si tu te retrouve avec une mémoire alloué sur plusieurs pages non contigues, si qui peutce produire avec getmem, les fonctions ne marcherons plus.
J'ai en effet eu le souci sans savoir d'où ça venait.
Maitenant, suivant la machine, et la mémoire dispo, ça peut marcher...
Enfin, je rajouterai que pour des petites images, la fonction GetBitmapBits marche très bien et elle est plus simple.
GetBitmapBits(hbitmap,count,pointeur)
count=nombre d'octets à copier
14 mai 2007 à 12:40
en fait, c'est pas moi qui invente ce bidouillage.
Dans l'unité Windows, tu trouve :
tagBITMAPINFO = packed record
bmiHeader: TBitmapInfoHeader;
bmiColors: array[0..0] of TRGBQuad;
end;
C'est une astuce un peut particulière pour préciser que le tableau bmicolors est bien juste après le reste en mémoire, mais qu'on ne connait pas la taille au moment de l'écriture du code.
Il faut donc réservé de la mémoire avec getmem par exemple puis lire le tableau en dehors des indices finalement.
Dans ce cas, pour avoir un talbeau bmicolors de 256 TRGBQuad il faut donc faire
var pinfo:PBitmapInfo;
getmem(pinfo,sizeof(bitinfo.bmiHeader)+256*sizeof(bitinfo.bmiColors))
C'est pas très simple, mais ça marche bien.
En regardant le code en assembleur produit, un array[0..0] et un pbytearray utilise toute les deux qu'une seule instruction. La différence vient de la zone mémoire utilisée.
@+
14 mai 2007 à 12:22
et si tu utilises PByte à la place de Array[0..0] tu divises le temps de traitement des données par 3
Et en plus le code devient plus simple.... Plus de tableau, le tout se fait avec un pointeur unique.
D'ailleurs d'ou sort tu ce bidouillage/bricolage (Array[0..0])?
HFR11:
Ce code permet un accès direct aux données(tous les pixels) d'un Bitmap par l'intermédiaire d'un pointeur unique.
Et si tu as du mal avec ce code c'est qu'il faudrait voir/revoir les bases sur les pointeurs.
@+
Cirec
14 mai 2007 à 11:50
En gros, le code ouvre une image bitmap, et recopie tout les octets de l'image dans un tableau.
A chaque clique sur l'image, la composante rouge est augmentée et les composantes bleu et vert sont diminuées dans le tableau.
Puis les octets du tableau sont transferés directement dans le Bitmap pour un affichage.
Ce code, comme le dit le titre, donne un exemple simple d'utilisation de GetDiBits et SetDiBits.
Barbichette
14 mai 2007 à 08:16
Tout ça a l'air très sympathique... Mais pensons aux débutants, aux curieux et à ceux qui veulent s'enrichier sans être obligés pour chaque code paru de déchiffrer l'algorythme. A quoi sert ce code ? Que fait ce code ?
Merci...
Bon courage et bonne bourre, cordialement, Patrice
14 mai 2007 à 01:52
Pour le test de performance, j'ai deux réponses :
Dans ma source Flames version 2, j'ai remplacé les scanline par ce systeme, 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 :
=unit Graphics====
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;
end;
==================
J'ai par ailleurs créé un programme que je n'ai pas mis ici qui trace des ellipses avec l'algorithme de Bresenham.
j'affiche une série de 1400 cercles deux fois plus vite.
Une autre raison à celà :
je dessine les 1400 cercles en mémoire et je met à jour le tout en une seule fois
Par contre, avec des scanline[] et des pixel[], l'image est mise à jour à chaque changement de pixel.
Barbichette
12 mai 2007 à 22:31
je pense que même si l'utilisation de Get/SetDiBits n'est pas courrante
ça reste quand même à un niveau débutant !
Au niveau du code:
!!!!!! << ImgResult.Picture.Bitmap := TBitmap.Create; >> !!!!!!
Cette ligne est source de fuite de mémoire ... en effet le bitmap ainsi crée
n'est jamais liberé.
Et array[0..0] c'est pas top comme bidouille sans compter que ça complique les choses ...
Donc voici un code modifié:
1: il charge le Bitmap depuis le fichier
(pour l'exemple j'ai enregistrer ton image dans le répertoire du projet sous "Img.bmp"
avec l'inspecteur d'objets)
2: ImgBitInfo est chargé depuis le fichier BMP
3: Utilisation de PByte à la place de Array[0..0]. Le code devient beaucoup plus simple
et plus rapide (la lecture et l'écriture ne se fait plus byte à byte mais par bloc
de 3 ou 4 bytes en fonction du PixelFormat(pf24Bit ou pf32Bit) du BMP chargé).
4: Après avoir enregistrer le BMP sur le disque et apporté les modifications on peut
supprimer ImgSrc
Le Code ...
type
TForm1 = class(TForm)
ImgResult: TImage;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure ImgResultMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
// Suppression de Array[0..0] Of byte
// au profit de PByte ce qui allège le code
// TArrayInt = array[0..0] of byte;
// PArrayInt = ^TArrayInt;
var
Form1: TForm1;
ImgBitInfo: TBitmapInfo; // structure pour getdibits et setdibits
PImgArrayPix: PByte;//PArrayInt; // buffer de l'image
BytesPerPixel,
BmpSrcWidth, BmpSrcHeight: Integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
Var
MS : TMemoryStream;
Bmp : TBitmap;
begin
{$IFDEF VER180} // Turbo Delphi
ReportMemoryLeaksOnShutdown := True;
{$ENDIF}
MS := TMemoryStream.Create;
With MS do
Try
LoadFromFile('img.BMP');
BMP := TBitmap.Create;
Try
BMP.LoadFromStream(MS);
MS.Position := SizeOf(TBITMAPFILEHEADER);
ReadBuffer(ImgBitInfo, SizeOf(TBITMAPINFO));
BytesPerPixel := ImgBitInfo.bmiHeader.biBitCount Shr 3;
BmpSrcWidth := BMP.Width;
BmpSrcHeight := BMP.Height;
GetMem(PImgArrayPix, BmpSrcWidth * BmpSrcHeight * BytesPerPixel);
GetDIBits(BMP.Canvas.Handle, BMP.Handle, 0, BmpSrcHeight, PImgArrayPix,
ImgBitInfo, DIB_RGB_COLORS);
ImgResult.Picture.Assign(BMP);
Finally
BMP.Free;
End;
Finally
Free;
End;
end;
procedure TForm1.ImgResultMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
FPosition,
clData,
BytesTotal: integer;
Pix : TRGBQuad ABSOLUTE clData;
begin
BytesTotal := BmpSrcWidth * BmpSrcHeight * BytesPerPixel;
FPosition := 0;
clData := 0;
Repeat
Move(Pointer(Longint(PImgArrayPix) + FPosition)^, clData, BytesPerPixel);
With Pix do Begin // pour respecter le même effet
If rgbBlue >= 10 Then Dec(rgbBlue, 10) Else rgbBlue := 0;
If rgbGreen >= 10 Then Dec(rgbGreen, 10) Else rgbGreen := 0;
If rgbRed <= 235 Then Inc(rgbRed, 20) Else rgbRed := 255;
End;
//Pour un autre effet
// Dec(Pix.rgbBlue, 10);
// Dec(Pix.rgbGreen, 10);
// Inc(Pix.rgbRed, 20);
Move(clData, Pointer(Longint(PImgArrayPix) + FPosition)^, BytesPerPixel);
Inc(FPosition, BytesPerPixel);
Until FPosition+BytesPerPixel > BytesTotal;
// Transférer tous les bits du buffer à l'image resultat:
SetDIBits(ImgResult.Canvas.Handle, ImgResult.Picture.Bitmap.Handle, 0, BmpSrcHeight,
PImgArrayPix, ImgBitInfo, DIB_RGB_COLORS);
// on met à jour l'image:
ImgResult.Refresh;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// Libérer le buffer :
FreeMem(PImgArrayPix);
end;
end.
Et le code est encore plus simple à comprendre
Bonne prog ...
@+
Cirec
10 mai 2007 à 19:15
Cependant, as-tu fait des mesures de performances avec ScanLine[]. Si oui, est-ce vraiment plus rapide ou bien le gain apporté n'est que mineur ?
En tout cas bravo !
++
Flo