GDI+, grosses images, mosaique...

Signaler
Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Modérateur
Dernière intervention
30 octobre 2008
-
Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Modérateur
Dernière intervention
30 octobre 2008
-
Bonjour a tous,

Voila, j'affiche dans un panel une image, que je peux resizer. Quand la longueur ou la hauteur de l'image est plus petite que celle du panel, je la raffiche au dessus et en dessous (affichage en mosaïque, quoi)
Mais le problème, c'est que parfois, ce sont des bitmap qui peuvent peser tres lourds (500 ko a 1 Mo...)
Autant vous dire que :
Plus l'image est affichée de fois, plus la taille en mémoire augmente
Plus l'image est lourde, plus l'affichage de la mosaïque est lent. (GDI+ en managé, c'est folklo )

Voici deja un bout de code :

private Image picture;
private Point pictureLocation; // Endroit ou on dessinera l'image
private Size pictureSize; // Taille de l'image
private Rectangle pictureDisplayRect; // Associe Picturelocation et Picturesize

private byte mosaicRight = 0;
private byte mosaicDown = 0;

...


public
void Zoom(int zoom)
{
pictureSize.Width += (int)(0.02f * (pictureSize.Width / 100) * zoom);
pictureSize.Height += (int)(0.02f * (pictureSize.Height / 100) * zoom);
ModifyRects();
}


private void ModifyRects()
{
pictureDisplayRect = new Rectangle(pictureLocation, pictureSize);
this.Invalidate();
}


private void Mosaique(Graphics g)
{
if(mosaicRight < 255 && pictureDisplayRect.X + (mosaicRight * pictureDisplayRect.Size.Width) < this.Size.Width)
++mosaicRight;
if(mosaicRight > 0 && (mosaicRight) * pictureDisplayRect.Size.Width > this.Size.Width)
--mosaicRight;
if(mosaicDown < 255 && (mosaicDown) * pictureDisplayRect.Size.Height < this.Size.Height)
++mosaicDown;
if(mosaicDown > 0 && (mosaicDown) * pictureDisplayRect.Size.Height > this.Size.Height)
--mosaicDown;


for(int i = 0; i <= mosaicRight; i++)
{
for(int j = 0; j <= mosaicDown; j++)
{
g.DrawImage(picture, pictureDisplayRect.X + (i * pictureDisplayRect.Width),
pictureDisplayRect.Y + (j * pictureDisplayRect.Height),
pictureDisplayRect.Width,
pictureDisplayRect.Height);
}
}
}

La méthode Mosaïque est appelée par la fonction onPaint, et la méthode Zoom par le mouvement de la molette (c'est ainsi qu'on resize l'image)
Donc l'appel des méthodes se fait ainsi
Zoom -> ModifyRects -> OnPaint -> Mosaique

Comme je l'ai dit, c'est vraiment loin d'etre rapide tout ca... surtout qu'apres je drois rafficher une autre image par dessus, et du dessin...
Alors j'ai cherché plusieurs pistes.
Premierement, le code unsafe. Ca me paraissait la meilleure façon d'optimiser la chose. Cependant, je fait aucun reel "traitement" sur l'image, je l'affiche ou j'en affiche plusieurs. Donc je suis toujours dépendant de la méthode DrawImage... Les pointeurs ne peuvent pas me servir. (si ?)
Je me suis donc dit que j'allais charger l'image dans un MemoryStream, et que j'éeffectuerais a la volée le resize d'image dedans, ainsi, plus l'image serait petite, moins elle peserait, et moins ce serait lent...Que neni, a force de recreer l'image a chaque mouvement de molette, ca améliore rien du tout... loin de la !

Alors je fais appel a vous :
Auriez vous une bonne technique pour améliorer tout ca ? Comment procéderiez vous ?
Pour ce qui est de l'algorithme en mosaique, vous avez pas un exemple meilleur ? (sachant que la, ce n'est que le début, ca sert juste a remplir sur la droite et le bas, mais il faudrait aussi que je remplisse en haut et a gauche, et quand je "bouge l'image", pas seulement que je la retrecit)

Merci par avance de votre aide
Mx

8 réponses

Messages postés
6351
Date d'inscription
samedi 1 juin 2002
Statut
Modérateur
Dernière intervention
2 août 2014
85
Déjà tu devrais utiliser un buffer : par exemple tu dessines ta mosaique sur une instance de Bitmap que tu dessines en un coup après avec DrawImageUnscaled

Cocoricoooooooo !!!!
coq
MVP Visual C#
Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Modérateur
Dernière intervention
30 octobre 2008
46
J'utilises deja un buffer (qui est le graphics g passé en parametre).

Pour le DrawImageUnscaled, je le fais lors du dessin du buffer, dans la
méthode Paint(). Par contre, si j'utilises le DrawImageUnscaled() dans
la creation de la mosaique, ca bug. (mais j'ai pas essayé de corriger,
faudrait que je vois ca)



Je me suis tourné vers l'api win32 et BitBlt. Ca améliore beaucoup les
perfs en ce qui concerne l'affichage en mosaïque. Par contre, quand il
n'y a l'image dessinée qu'une seule fois, on dirait que c'est plus lent
que le DrawImage().

Jvais encore continuer a chercher, Mes nouvelles decouvertes sont
toutes chaudes la, y'a surement moyen d'améliorer ca encore un peu.



Merci pour ton intervention coq


Mx
Messages postés
6351
Date d'inscription
samedi 1 juin 2002
Statut
Modérateur
Dernière intervention
2 août 2014
85
Ok
Juste par curiosité tu veux faire quoi au juste ?

Cocoricoooooooo !!!!
coq
MVP Visual C#
Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Modérateur
Dernière intervention
30 octobre 2008
46
Il faut que je remplisse une forme quelconque par une image, ou plusieurs fois cette image selon sa taille.

Donc je peux déplacer l'image et la redimensionner, la contrainte
principale etant qu'il n'y ait pas de "zone blanche" dans cette forme
grace au dessin en mosaïque (ce qui n'est pas encore le cas, avec ma
fonction)



Pour info, voici ma méthode Paint



protected override void OnPaint(PaintEventArgs e)

{

if(backBuffer == null)


backBuffer = new Bitmap(this.ClientSize.Width,
this.ClientSize.Height);



Graphics g = Graphics.FromImage(backBuffer);

g.Clear(this.BackColor);



if(highQuality)

{

g.SmoothingMode = SmoothingMode.HighQuality;


g.InterpolationMode =
InterpolationMode.HighQualityBicubic;

g.PixelOffsetMode = PixelOffsetMode.HighQuality;

}



if(picture != null)

Mosaique(g);


//g.DrawImage(picture, pictureDisplayRect,
pictureRect, GraphicsUnit.Pixel);



...(Autres dessins dans g)



g.Dispose();

e.Graphics.DrawImageUnscaled(backBuffer, 0, 0);

}



La variable HighQuality etant un booleen qui est false lors du
mouseDown (Un mouseMove permet de bouger l'image, donc autant ne pas
dessiner l'image en haute qualité pendant le déplacement de celle ci).



Les images sont du type 945 x 1890 x 24 en Jpg (600 ko pour cet exemple)(j'avais dit bitmap dans mon premier post)

Si tu as d'autres astuces pour améliorer les perfs, Je suis preneur ;)


Mx
Messages postés
74
Date d'inscription
lundi 30 juin 2003
Statut
Membre
Dernière intervention
18 octobre 2006
2
Apparement tu peut utiliser les pointeurs , avec la methode LockBits et UnLockBits pour travailler en memoire dessus.



AP
Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Modérateur
Dernière intervention
30 octobre 2008
46
Oui oui, mais, pour quel "travail en mémoire" je pourrais
utiliser ces lockbits, sachant que la seule chose que je fais c'est
afficher des images ?

J'étais parti pour cette solution, mais j'ai pas trouvé dans quel cas je pourrais m'en servir ... ?


Mx
Messages postés
41
Date d'inscription
mardi 30 septembre 2003
Statut
Membre
Dernière intervention
21 avril 2011

Salut,
s'il ne s'agit que de l'affichage pourquoi n'utilises-tu pas directx?

Tuhabites.
Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Modérateur
Dernière intervention
30 octobre 2008
46
Si tu veux, si ca avait été pour moi, je l'aurais certainement fait en DirectX.

Mais bon, la tu vois, c'est une appli "de bureau", destiné pour un parc
de PC tres hétérogène, parfois avec des cartes graphiques peut-etre
vieilles ou désuettes.

Donc je préfère que ce type de manipulation soit traitée "softwarement".



Bitblt répond plutot a mes attentes, donc je pense que je vais l'adopter.

Je ferais une source, je pense la semaine prochaine, avec un exemple de son utilisation.



Petite question, sachant que j'utilises les fonctions la dll "gdi32.dll", est-ce que ce sera toujours géré par longhorn ?

Mx