[SOLVED] Arbeitsspeicher läuft voll



  • Hi.

    Ich habe eine abgeleitete PictureBox in welche ich ein Bitmap lade
    und dieses dann über das Mausrad zoom möchte.

    Folgenden Code habe ich dafür:

    public static void MouseWheele(System.Windows.Forms.MouseEventArgs e, ref double ZoomFactor, DynamicPictureBox DPB)
            {
                System.Drawing.Size NewSize;
                ZoomFactor *= Math.Exp(0.1 * (double)e.Delta / 120.0);
                NewSize = DPB.Size;
                NewSize.Height = (int)((double)NewSize.Height * ZoomFactor);
                NewSize.Width = (int)((double)NewSize.Width * ZoomFactor);
                if (DPB.Image != null)
                {
                    DPB.Image = (System.Drawing.Image)new System.Drawing.Bitmap(DPB.GetDisplayedBitmap(), NewSize);
                }
            }
    

    Dabei läuft aber mein Arbeitsspeicerh voll. Ausschlaggebend ist dafür die Funktion DPB.GetDisplayeBitmap();

    public System.Drawing.Bitmap GetDisplayedBitmap() {
                if (DisplayedImage != null)
                {
                    return new System.Drawing.Bitmap(DisplayedImage);
                }
    
                return null;
            }
    

    Wieso werden die alten Bitmaps nicht verworfen und a.d. Garbage Collector geworfen? Außerdem ist der code relativ langsam.

    Grüße,
    Jan



  • System.Drawing.Bitmap implementiert IDisposable , also mußt du auch Dispose() aufrufen, wenn du das Bitmap nicht mehr brauchst. Siehe auch hier.

    (Daß der GC die Bitmaps nicht wegräumt, liegt daran, daß Bitmap eine GDI+-Routine verwendet, um den Bitmap-Speicher zu verwalten. Weil es keine verwaltete Allokation ist, sieht der GC nicht, daß vergleichsweise viel Speicher an dem Objekt hängt.)

    Nimm den Profiler, um rauszufinden, warum der Code langsam ist. Meine Vermutung: weil Bitmap (wie allgemein System.Drawing ) auf GDI+ basiert, was anders als GDI oder Direct2D ohne Hardwareunterstützung implementiert ist, und weil du in GetDisplayedBitmap() unnötigerweise eine Kopie von DisplayedImage erstellst.



  • Hi,

    danke, das habe ich jetzt auch schon umgesetzt. Und ich bekomme jetzt eine Fehlermeldung, die ich aber reproduzieren kann. Folgender Code:

    public class DynamicPictureBox : System.Windows.Forms.PictureBox
        {
            public Main Central = null;
            private double ZoomFactor = 1.0;
            private System.Windows.Forms.ContextMenuStrip MyContextMenuStrip = null;
            private System.Drawing.Bitmap OriginalBitmapVar = null;
    
            public DynamicPictureBox(Main fCentral) : base()
            {
                Central = fCentral;
                System.Windows.Forms.ToolStripItem ti;
    
                #region Settings
                this.Dock = System.Windows.Forms.DockStyle.Fill;
                this.BackColor = System.Drawing.Color.White;
                this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
                #endregion
    
                #region Context menu strip
                MyContextMenuStrip = new System.Windows.Forms.ContextMenuStrip();
                ti = MyContextMenuStrip.Items.Add("Load picture");
                ti.Click += new EventHandler((sender, e) => DynamicPictureBoxContextMenuFunctions.loadPicture_Click(sender, e, this));
    
                ti = MyContextMenuStrip.Items.Add("Clear picture");
                ti.Click += new EventHandler((sender, e) => DynamicPictureBoxContextMenuFunctions.clearPicture_Click(sender, e, this));
    
                this.ContextMenuStrip = this.MyContextMenuStrip;
                #endregion
    
                this.MouseDown += new System.Windows.Forms.MouseEventHandler(DynamicPictureBox_MouseDown);
                this.MouseWheel += new System.Windows.Forms.MouseEventHandler(DynamicPictureBox_MouseWheel);
                this.MouseMove += new System.Windows.Forms.MouseEventHandler(DynamicPictureBox_MoveMouseDown);
                this.MouseHover += new EventHandler(this.DynamicPictureBox_MouseHover);
            }
    
            public int LoadBitMap(string Filename)
            {
                this.OriginalBitmapVar = new System.Drawing.Bitmap(Filename);
                this.Image = this.OriginalBitmapVar;
                if (Central.VerticalSplitterLevel0.Width > Image.Width + 200) {Central.VerticalSplitterLevel0.SplitterDistance = Image.Width;}
                return Image.Size.Width;
            }
    
            public void ClearImage() {
                this.Image = null;
                this.OriginalBitmapVar.Dispose();
                this.OriginalBitmapVar = null;
            }
    
            public System.Drawing.Bitmap OriginalBitmap() { return this.OriginalBitmapVar; }
    
            public System.Drawing.Bitmap OriginalBitmap(System.Drawing.Bitmap fBMP) { this.OriginalBitmapVar = fBMP;  return this.OriginalBitmapVar; }
    
            private void DynamicPictureBox_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
            {
                MouseInteraction.MouseDown(e, this);
            }
    
            private void DynamicPictureBox_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
            {
                MouseInteraction.MouseWheele(e, ref this.ZoomFactor, this, ref this.OriginalBitmapVar);
            }
    
            private void DynamicPictureBox_MouseHover(object sender, EventArgs e)
            {
                this.Focus();
            }
        }
    

    Und der MouseWheel-Event:

    public static void MouseWheele(System.Windows.Forms.MouseEventArgs e, ref double ZoomFactor, DynamicPictureBox DPB, ref System.Drawing.Bitmap fBMP)
            {
                System.Drawing.Size NewSize;
                ZoomFactor *= Math.Exp(0.1 * (double)e.Delta / 120.0);
                DPB.Central.GetOutputTabPage().WriteText("New zoomfactor: " + ZoomFactor);
                NewSize = DPB.Size;
                NewSize.Height = (int)((double)NewSize.Height * ZoomFactor);
                NewSize.Width = (int)((double)NewSize.Width * ZoomFactor);
    
                if (DPB.Image != null)
                {
                    DPB.Image.Dispose();
                    DPB.Image = new System.Drawing.Bitmap(fBMP, NewSize); //ERROR!
                }
            }
    

    Der Fehler tritt auf, wenn ich vor der neuen Erstellung des Bitmaps das Image Dispose. Dabei wird wohl auch die private Variable "OriginalBitmap" zerstört.
    Ich kann das jetzt natürlich zwischenspeichern... aber dann wird ja wieder der Kopierkonstruktor "unnötig" oft aufgerufen.

    Wie wäre hier die "eleganteste" Vorgehensweise?

    Unterm Strich möchte ich ein Bitmap laden und dann in der Lage sein, dieses über das Mausrad zu zoomen und über drücken der mittleren Maustaste zu verschieben. Vielleicht gibts ja noch einen generell viel eleganteren Weg.



  • Hi.

    Habe es gelöst - danke für den Tip.

    Folgender Code:

    public class DynamicPictureBox : System.Windows.Forms.PictureBox
        {
            public Main Central = null;
            private double ZoomFactor = 1.0;
            private System.Windows.Forms.ContextMenuStrip MyContextMenuStrip = null;
            private System.Drawing.Bitmap OriginalBitmapVar = null;
    
            public DynamicPictureBox(Main fCentral) : base()
            {
                Central = fCentral;
                System.Windows.Forms.ToolStripItem ti;
    
                #region Settings
                this.Dock = System.Windows.Forms.DockStyle.Fill;
                this.BackColor = System.Drawing.Color.White;
                this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
                #endregion
    
                #region Context menu strip
                MyContextMenuStrip = new System.Windows.Forms.ContextMenuStrip();
                ti = MyContextMenuStrip.Items.Add("Load picture");
                ti.Click += new EventHandler((sender, e) => DynamicPictureBoxContextMenuFunctions.loadPicture_Click(sender, e, this));
    
                ti = MyContextMenuStrip.Items.Add("Clear picture");
                ti.Click += new EventHandler((sender, e) => DynamicPictureBoxContextMenuFunctions.clearPicture_Click(sender, e, this));
    
                this.ContextMenuStrip = this.MyContextMenuStrip;
                #endregion
    
                this.MouseDown += new System.Windows.Forms.MouseEventHandler(DynamicPictureBox_MouseDown);
                this.MouseWheel += new System.Windows.Forms.MouseEventHandler(DynamicPictureBox_MouseWheel);
                this.MouseMove += new System.Windows.Forms.MouseEventHandler(DynamicPictureBox_MoveMouseDown);
                this.MouseHover += new EventHandler(this.DynamicPictureBox_MouseHover);
            }
    
            public int LoadBitMap(string Filename)
            {
                this.OriginalBitmapVar = new System.Drawing.Bitmap(Filename);
                this.Image = (System.Drawing.Bitmap)this.OriginalBitmapVar.Clone();
                if (Central.VerticalSplitterLevel0.Width > Image.Width + 200) {Central.VerticalSplitterLevel0.SplitterDistance = Image.Width;}
                return Image.Size.Width;
            }
    
            public void ClearImage() {
                this.Image = null;
                this.OriginalBitmapVar.Dispose();
                this.OriginalBitmapVar = null;
            }
    
            public System.Drawing.Bitmap OriginalBitmap() { return this.OriginalBitmapVar; }
    
            public System.Drawing.Bitmap OriginalBitmap(System.Drawing.Bitmap fBMP) { this.OriginalBitmapVar = fBMP;  return this.OriginalBitmapVar; }
    
            private void DynamicPictureBox_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
            {
                MouseInteraction.MouseDown(e, this);
            }
    
            private void DynamicPictureBox_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
            {
                MouseInteraction.MouseWheele(e, ref this.ZoomFactor, this, ref this.OriginalBitmapVar);
            }
    
            private void DynamicPictureBox_MouseHover(object sender, EventArgs e)
            {
                this.Focus();
            }
        }
    

    ...und...

    public static void MouseWheele(System.Windows.Forms.MouseEventArgs e, ref double ZoomFactor, DynamicPictureBox DPB, ref System.Drawing.Bitmap fBMP)
            {
                System.Drawing.Size NewSize;
                ZoomFactor *= Math.Exp(0.1 * (double)e.Delta / 120.0);
                DPB.Central.GetOutputTabPage().WriteText("New zoomfactor: " + ZoomFactor);
                NewSize = DPB.Size;
                NewSize.Height = (int)((double)NewSize.Height * ZoomFactor);
                NewSize.Width = (int)((double)NewSize.Width * ZoomFactor);
    
                if (DPB.Image != null)
                {
                    DPB.Image.Dispose();
                    DPB.Image = new System.Drawing.Bitmap(fBMP, NewSize);
                }
            }
    

    Ist jetzt auch deutlich schneller... vor allem ist es schnell genug 🙂
    Habt ihr einen eleganteren Vorschlag? Damit könnte ich aber erstmal leben...


Log in to reply