Wrapper .net 2.0 pour les cachedbitmap de gdi+

Soyez le premier à donner votre avis sur cette source.

Vue 7 062 fois - Téléchargée 367 fois

Description

Si le Framework .NET 3.0 et supérieur possèdent une classe CachedBitmap managée, ce n'est malheureusement pas le cas de .NET 2.0.
Ma petite source est là pour y remédier : elle permet d'utiliser les CachedBitmap pour dessiner rapidement sur un objet Graphics.
Les CachedBitmap sont censées être plus rapides à dessiner qu'en passant par la méthode Graphics.DrawImage()
Suite à la demande de Kevin Ory, je me suis mis au travail et voice cette source.
Elle est composée de 2 parties : une librairie ManagedCachedBitmap en C# et une petite application de test en VB .NET

Source / Exemple :


using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Reflection;

namespace ManagedCachedBitmap
{
    /// <summary>
    /// Wrapper pour les CachedBitmap non managés de GDI+
    /// </summary>
    public class ManagedCachedBitmap
    {
        [DllImport("gdiplus.dll")]
        public static extern int GdipCreateCachedBitmap(IntPtr pBitmap, IntPtr pGraphics, ref IntPtr pCachedBitmap);
        [DllImport("gdiplus.dll")]
        public static extern int GdipDrawCachedBitmap(IntPtr pGraphics, IntPtr pCachedBitmap, int x, int y);
        [DllImport("gdiplus.dll")]
        public static extern int GdipDeleteCachedBitmap(IntPtr pCachedBitmap);

        private Bitmap _cachedBitmap;
        private Graphics _graphics;
        private IntPtr _pCachedBitmap;
        private FieldInfo _Bitmapfi;
        private FieldInfo _Graphicsfi;
        private IntPtr _pBitmapfi;
        private IntPtr _pGraphicsfi;
        private Boolean _isLoaded = false;

        /// <summary>
        /// Constructeur.
        /// </summary>
        /// <param name="b">un objet Bitmap</param>
        /// <param name="g">un objet Graphics</param>
        public ManagedCachedBitmap(Bitmap b, Graphics g)
        {
            this.Init(b, g);
        }

        /// <summary>
        /// Destructeur
        /// </summary>
        ~ManagedCachedBitmap()
        {
            this.Delete();
        }

        /// <summary>
        /// Utilisé pour initialiser un CachedBitmap à partir d'un Graphics et d'un Bitmap
        /// </summary>
        /// <param name="b">un objet Bitmap</param>
        /// <param name="g">un objet Graphics</param>
        public void Init(Bitmap b, Graphics g)
        {
            if (_isLoaded) this.Delete();
            this._cachedBitmap = b;
            this._graphics = g;
            this._Bitmapfi = typeof(Bitmap).GetField("nativeImage", BindingFlags.Instance | BindingFlags.NonPublic);
            this._pBitmapfi = (IntPtr)this._Bitmapfi.GetValue(this._cachedBitmap);
            this._Graphicsfi = typeof(Graphics).GetField("nativeGraphics", BindingFlags.Instance | BindingFlags.NonPublic);
            this._pGraphicsfi = (IntPtr)this._Graphicsfi.GetValue(this._graphics);
            GdipCreateCachedBitmap(this._pBitmapfi, this._pGraphicsfi, ref this._pCachedBitmap);
            this._isLoaded = true;
        }

        /// <summary>
        /// Modifie seulement le Bitmap sans avoir à tout refaire (plus rapide que la méthode Init() ). Utile pour faire une animation par exemple.
        /// </summary>
        /// <param name="b">Un objet Bitmap</param>
        /// <returns></returns>
        /// <remarks>La mise à jour ne sera pas effectuée si la méthode</remarks>
        public Boolean InitBitmap(Bitmap b)
        {
            if (!_isLoaded) return false;
            this.Delete();
            this._cachedBitmap = b;
            this._Bitmapfi = typeof(Bitmap).GetField("nativeImage", BindingFlags.Instance | BindingFlags.NonPublic);
            this._pBitmapfi = (IntPtr)this._Bitmapfi.GetValue(this._cachedBitmap);
            GdipCreateCachedBitmap(this._pBitmapfi, this._pGraphicsfi, ref this._pCachedBitmap);
            this._isLoaded = true;
            return true;
        }

        /// <summary>
        /// Modifie seulement le Graphics où l'on dessinera le Bitmap (plus rapide que la méthode Init() ).
        /// </summary>
        /// <param name="g"></param>
        /// <returns></returns>
        public Boolean InitGraphics(Graphics g)
        {
            if (!_isLoaded) return false;
            this.Delete();
            this._graphics = g;
            this._Graphicsfi = typeof(Graphics).GetField("nativeGraphics", BindingFlags.Instance | BindingFlags.NonPublic);
            this._pGraphicsfi = (IntPtr)this._Graphicsfi.GetValue(this._graphics);
            GdipCreateCachedBitmap(this._pBitmapfi, this._pGraphicsfi, ref this._pCachedBitmap);
            this._isLoaded = true;
            return true;
        }

        /// <summary>
        /// Définit ou renvoie le Bitmap utilisé
        /// </summary>
        public Bitmap Bitmap
        {
            get
            {
                return _cachedBitmap;
            }
            set
            {
                this._cachedBitmap = value;
                this.InitBitmap(this._cachedBitmap);
            }
        }

        /// <summary>
        /// Définit ou renvoie le Graphics utilisé
        /// </summary>
        public Graphics Graphics
        {
            get
            {
                return _graphics;
            }
            set
            {
                this._graphics = value;
                this.InitGraphics(this._graphics);
            }
        }

        /// <summary>
        /// Vrai si l'initialisation du CachedBitmap s'est déroulée correctement
        /// </summary>
        public bool IsLoaded
        {
            get
            {
                return _isLoaded;
            }
        }

        /// <summary>
        /// Dessine le Bitmap sur l'objet Graphics aux coordonnées (0 ; 0)
        /// </summary>
        /// <returns>Un entier</returns>
        public int Draw()
        {
            return this.Draw(0, 0);
        }

        /// <summary>
        /// Dessine le Bitmap sur l'objet Graphics aux coordonnées spécifiées
        /// </summary>
        /// <param name="x">Distance X du bord gauche du Graphics</param>
        /// <param name="y">Distance Y du bord haut du Graphics</param>
        /// <returns>Un entier</returns>
        public int Draw(int x, int y)
        {
            return GdipDrawCachedBitmap(this._pGraphicsfi, this._pCachedBitmap, x, y);
        }

        /// <summary>
        /// Dessine le bitmap sur l'objet Graphics au coin haut-gauche spécifié par le Point
        /// </summary>
        /// <param name="p">Un objet Point qui désigne le coin haut-gauche où sera dessiné le Bitmap</param>
        /// <returns>Un entier</returns>
        public int Draw(Point p)
        {
            return this.Draw(p.X, p.Y);
        }

        /// <summary>
        /// Libère les ressources utilisées par le CachedBitmap non managé.
        /// </summary>
        public void Delete()
        {
            GdipDeleteCachedBitmap(this._pCachedBitmap);
            this._isLoaded = false;
        }

    }
}

Conclusion :


Après quelques tests, il apparait que la méthode des CachedBitmap est rapide, mais à peu près autant que la méthode managée disponible en .NET 2.0 Graphics.DrawImageUnscaled(). L'utilité de ma source est donc sans doute limitée mais en outre la source en elle-même est intéressante je pense.

Codes Sources

A voir également

Ajouter un commentaire

Commentaire

Kevin.Ory
Messages postés
843
Date d'inscription
mercredi 22 octobre 2003
Statut
Membre
Dernière intervention
7 janvier 2009
5 -
Salut,

Excellent boulot ça, merci :)

C'est effectivement ce que je recherchais (je crois, je n'ai pas encore analysé ton code en détails), mais j'avais abandonné l'idée d'essayer d'améliorer mon programme .NET 2, et je suis passé à .NET 3.5. As-tu déjà essayé WPF? C'est assez magnifique, ça s'utilise un peu comme DirectX (logique, puisque c'est du DirectX), et à évidement des performances incomparable avec GDI32 (Plusieurs images de haute résolution avec mouvement et zoom temps réel sans problèmes).
Mais j'ai juste essayé, je ne consacre plus bcp de temps à la prog ces temps, mais vais bien m'y remettre un jour :)

Pour ce qui est ton code, comme tu le dis il est très intéressant car nous permet de comprendre comment wrapper un objet non managé, mais dommage que les performances ne soit pas à la hauteur de ce que j'attendais. Je vais tester tout ça en détail un de ces prochains jours (et je noterai après ^^).

Encore merci, et bonne continuation à toi..

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.