1 votes

Comment écrire dans un tampon d'image (Bitmap ?) pour des affichages GDI+ plus rapides ?

En utilisant C++ et .net, j'ai un flux de données que je veux afficher comme une image défilante. Chaque fois que je reçois de nouvelles données, je veux les ajouter à une nouvelle ligne (128x1 pixels) et faire défiler le contenu précédent sur le côté.

Ma première tentative de résolution du problème consistait à rendre l'ensemble des données à chaque fois que j'obtenais une nouvelle ligne. Cela a fonctionné, mais c'était beaucoup trop lent. Je pense donc qu'il serait plus logique d'écrire dans une sorte de tampon (un bitmap peut-être ?). Le problème est que je ne vois pas comment je peux faire cela ; Graphic vous permettent de dessiner sans problème, mais je ne vois pas de moyen évident d'indiquer à mon contrôle d'utiliser un objet Bitmap comme tampon ? De même, je ne vois pas comment dessiner sur un bitmap que je pourrais ensuite écrire sur l'écran.

Cela doit être possible, mais mon google-foo m'a fait défaut jusqu'à présent...

[Edit1] Juste pour clarifier, les données sont un spectrogramme. L'image suivante montre le genre de chose que j'essayais d'obtenir :

alt text

Les données que je trace sont des tableaux de flottants. Il n'y a rien pour limiter le nombre de données que je vais obtenir, donc je veux juste oublier les données lorsqu'elles tombent sur le côté du graphique.

Je suis actuellement en train d'hériter d'un System::Windows::Forms::UserControl mais pourrait passer à autre chose s'il y a une meilleure alternative ?

5voto

Paul Alexander Points 17611

Jetez un coup d'œil à la ScrollWindow méthode win32. Vous pouvez faire défiler les données existantes à l'écran, puis dessiner uniquement les nouvelles données. Cette méthode est très rapide.

1voto

Bitmap bmpImage = nouveau Bitmap(512,512) ;

for (int iRow = 0 ; iRow < 512 ; iRow++)

{

  for (int iCol = 0; iCol <512; iCol++)
                        {
                            Color clr;
                            bmpImage.SetPixel(iCol, iRow, clr);
                        }

}

(Image)bmpImage.save()

1voto

BeeWarloc Points 31

Une stratégie possible :

  • Dessinez dans un tampon arrière de gauche à droite, qui s'enroule lorsqu'il atteint la fin. Exécuter la logique de défilement uniquement lors de la peinture à l'écran (à un taux de rafraîchissement spécifié). Utilisez DrawImage avec un rectangle source et un rectangle destination pour y parvenir.

  • Utilisez la méthode bitmap.LockBits(...) et bitmap.UnlockBits(...) pour modifier les données brutes du Bitmap. Veillez à ne verrouiller que le rectangle que vous allez modifier, car ces fonctions effectuent des copies des données bitmap de la mémoire non gérée à la mémoire gérée. Un exemple de la manière de procéder est décrit ici Méthode Bitmap : :.LockBits (Rectangle, ImageLockMode, PixelFormat) .

  • Une alternative à LockBits est d'utiliser SetPixel sur le bitmap. Mais SetPixel est connu pour être lent.

  • Lors du blitting des images à l'écran, assurez-vous que CompositingMode de l'instance Graphics est défini sur bmpg.CompositingMode = CompositingMode.SourceCopy, et que le format de pixel du tampon arrière est PixelFormat.Format32bppPArgb.

0voto

Peter Points 5388

Je ne sais pas exactement ce que vous essayez de dessiner (une sorte de contrôle sur une boîte de dialogue ?) mais je pense que cela devrait fonctionner comme suit :

class Foo {
    ...
    Gdiplus::Bitmap* m_pBitmap;
};

void Foo::DrawItem(LPDRAWITEMSTRUCT lpDraw) {

   // update bitmap if needed
   if(some_condition_requiring_bitmap_redraw) {

       // do expensive drawing into bitmap
       Gdiplus::Graphics graphics(m_pBitmap);
   }

   // create a graphics object to draw the control from the bitmap
   Gdiplus::Graphics graphics(lpDraw->hDC);
   graphics.DrawImage(m_pBitmap, ...);
}

C'est une estimation très approximative de toute façon. L'appel à DrawItem peut être très différent si vous utilisez .NET (je ne le connais pas...), mais la logique de base devrait être à peu près la même.

En fonction de la nature exacte de vos données, il peut ne pas être efficace de dessiner des lignes d'un pixel à la fois. Il est peut-être préférable de dessiner une grande zone et de n'en afficher que des parties selon les besoins, mais cela dépend évidemment de la manière dont vos données sont reçues.

Il se peut également que vous deviez effectuer une sorte de mise à jour de votre bitmap pour faire "défiler" son contenu. Je vous laisse le soin de le faire :-)

0voto

Essayez ce qui suit :

  • Démarrez une nouvelle application VC++ WinForms.

  • Ajouter un contrôle utilisateur nommé 'Spectrogram' au projet.

  • Ajoutez un contrôle de minuterie au contrôle utilisateur "Spectrogram" et définissez la propriété "Enabled" sur true.

  • Ajoutez les variables privées suivantes au contrôle utilisateur "Spectrogram".

    private: Graphics ^m_gfxBuffer; Graphics ^m_gfxOriginal; Bitmap ^m_bmpBuffer; Bitmap ^m_bmpOriginal;

  • Ajoutez le code suivant au constructeur du "Spectrogram" :

    m_bmpBuffer = gcnew Bitmap(this->ClientSize.Width, this->ClientSize.Height); m_gfxBuffer = Graphics::FromImage(m_bmpBuffer); m_bmpOriginal = gcnew Bitmap(this->ClientSize.Width, this->ClientSize.Height); m_gfxOriginal = Graphics::FromImage(m_bmpOriginal); this->SetStyle(::ControlStyles::AllPaintingInWmPaint | ::ControlStyles::DoubleBuffer | ::ControlStyles::UserPaint | ::ControlStyles::OptimizedDoubleBuffer, true); this->UpdateStyles();

  • Ajoutez le code suivant à l'événement de peinture "Spectrogram" :

    array<unsigned char, 1> ^bytes = gcnew array<unsigned char, 1>(m_bmpBuffer->Height * 3); Random ^r = gcnew Random(); r->NextBytes(bytes);

    m_gfxOriginal->DrawImage(m_bmpBuffer, -1, 0);

    int y = 0; for (int i = 0; i < m_bmpOriginal->Height * 3; i += 3) { m_bmpOriginal->SetPixel(m_bmpOriginal->Width - 1, y++, ::Drawing::Color::FromArgb(255, bytes[i], bytes[i + 1], bytes[i + 2])); }

    m_gfxBuffer->DrawImage(m_bmpOriginal, 0, 0); e->Graphics->DrawImage(m_bmpOriginal, 0, 0);

  • Ajoutez le code suivant à l'événement tick du timer 'Spectrogram'.

    this->Invalidate(false);

  • Sauvegarder votre projet

  • Nettoyer et reconstruire

  • Exécuter le projet

  • Fermer le formulaire en cours

  • Le contrôle utilisateur Spectrogram doit maintenant se trouver dans la "boîte à outils".

  • Faites-le glisser de la "boîte à outils" vers le formulaire et vous devriez voir défiler une sorte de spectrogramme aux couleurs aléatoires.

Cela devrait vous donner une idée générale d'un contrôle à mémoire tampon bitmap. La clé ici est l'appel "SetStyle" dans le constructeur et le décalage du bitmap de -1 dans l'événement paint.

Vous devrez disposer correctement des objets graphiques et bitmap et gérer leur destruction et leur reconstruction dans l'événement de redimensionnement.

J'espère que cela vous aidera. Faites-moi savoir comment ça se passe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X