PictureBox zooming schlägt mit Speicherleg fehl

Asterix-Ac

Erfahrenes Mitglied
Hallo zusammen,

habe eine Zoom-Funktion für die Picturebox gebaut(Größe ändern) und wenn ich nun Bilder hereinlade, bekomme ich zwei Exceptions. Es sei dazu gesagt, dass ich ein zweites Form benutze(ShowDialog), um die Bilder zu sehen und je größer die Bilder sind, desto schneller bekomme ich die Exceptions(kleinerer Zoom). Hier die Exceptions :
1 :
Code:
Ausnahme System.ComponentModel.Win32Exception wurde im ausgeführten Programm ausgelöst:
Der Vorgang wurde erfolgreich beendet

CreateCompatibleDIB()
CreateBuffer()
AllocBuffer()
AllocBufferInTempManager()
Allocate()
WmPaint()
WndProc()
OnMessage()
WndProc()
DebuggableCallback()
System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop()
RunMessageLoopInner()
RunMessageLoop()
ShowDialog()
ShowDialog()
TsAnsichtGrossesBildClick() - g:\Projekte-dotnet-20\Bilder-Assistent\MainForm.cs:465,5
RaiseEvent()
OnClick()
HandleClick()
HandleMouseUp()
FireEventInteractive()
FireEvent()
OnMouseUp()
WmMouseUp()
WndProc()
WndProc()
WndProc()
OnMessage()
WndProc()
DebuggableCallback()
System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop()
RunMessageLoopInner()
RunMessageLoop()
Run()
Main() - g:\Projekte-dotnet-20\Bilder-Assistent\MainForm.cs:43,4
2 :
Code:
Ausnahme System.InvalidOperationException wurde im ausgeführten Programm ausgelöst:
BufferedGraphicsContext kann nicht entfernt werden, da momentan ein Puffervorgang läuft.

Dispose()
Dispose()
AllocBufferInTempManager()
Allocate()
WmPaint()
WndProc()
OnMessage()
WndProc()
DebuggableCallback()
System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop()
RunMessageLoopInner()
RunMessageLoop()
ShowDialog()
ShowDialog()
TsAnsichtGrossesBildClick() - g:\Projekte-dotnet-20\Bilder-Assistent\MainForm.cs:465,5
RaiseEvent()
OnClick()
HandleClick()
HandleMouseUp()
FireEventInteractive()
FireEvent()
OnMouseUp()
WmMouseUp()
WndProc()
WndProc()
WndProc()
OnMessage()
WndProc()
DebuggableCallback()
System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop()
RunMessageLoopInner()
RunMessageLoop()
Run()
Main() - g:\Projekte-dotnet-20\Bilder-Assistent\MainForm.cs:43,4
Also ich habe schon eine Menge probiert und Dispose während der Zoom-Vorgänge schon die Images und nutze DoubleBuffer für das Form, aber auch das hat nichts genutzt.

Bin ziemlich ratlos.

Asterix :confused:
 
Hi,

ich poste mal die komplette Zoom-Methode :

Code:
void TsZoomGroesserClick(object sender, EventArgs e)
{
	this.pictureBox1.Image.Dispose();
	int i;
	if(this.zoomAktuell == -1)
	{
		i=0;
		double prozent = (double)this.pictureBox1.Width / (double)this.imageOrgWidth;
		for(; i<zoom.Length-1;i++)
		{
			if(zoom[i] < prozent)
			{
//				MessageBox.Show(prozent.ToString());
				continue;
			}
			else
			{
				i+=1;
				break;
			}
		}				
		this.zoomAktuell = i;
//		MessageBox.Show(zoom[i].ToString());
	}
	else
	{
		if(zoomAktuell >= zoom.Length-2)
			return;
		i=this.zoomAktuell+1;
		this.zoomAktuell = i;
//		MessageBox.Show(zoom[i].ToString());
	}
	
	if(this.imageOrgHeight > this.imageOrgWidth)
	{
		this.pictureBox1.Height = (int)Math.Round(this.imageOrgHeight * zoom[i]);
		this.pictureBox1.Width = this.pictureBox1.Height * this.imageOrgWidth / this.imageOrgHeight;
	}
	else if(this.imageOrgWidth > this.imageOrgHeight)
	{
		this.pictureBox1.Width = (int)Math.Round(this.imageOrgWidth * zoom[i]);
		this.pictureBox1.Height = this.pictureBox1.Width * this.imageOrgHeight / this.imageOrgWidth;
	}
//-- ab hier versuche ich mich zur Zeit an der AutoScrollPostition - was noch nicht so ganz klappt :-)
	int x,y;
	if(this.pictureBox1.Width > this.panel1.Width && this.pictureBox1.Height > this.panel1.Height)
	{
		x = 0 - (this.pictureBox1.Width / 4);
		y = 0 - (this.pictureBox1.Height / 4);
		this.panel1.AutoScrollPosition = new Point((this.pictureBox1.Width / 4),(this.pictureBox1.Height / 4));
	}
	else if(this.pictureBox1.Width > this.panel1.Width)
	{
		x = 0 - (this.pictureBox1.Width / 4);
		y = 0;
		this.panel1.AutoScrollPosition = new Point((this.pictureBox1.Width / 4),0);
	}
	else if(this.pictureBox1.Height > this.panel1.Height)
	{
		x = 0;
		y = 0 - (this.pictureBox1.Height / 4);
		this.panel1.AutoScrollPosition = new Point(0,(this.pictureBox1.Height / 4));
	}
	else
	{
		x = (this.toolStripContainer1.Width/2)-(this.pictureBox1.Width/2);
		y = ((this.toolStripContainer1.Height-this.statusStrip1.Height)/2)-(this.pictureBox1.Height/2);
	}
	this.pictureBox1.Location = new System.Drawing.Point(x,y);
//-- bis hier geht der AutoScrollPosition-Versuch 	
	if(i >= zoom.Length-2)
	{
		this.tsZoomGroesser.Enabled = false;
	}
	this.tsZoomKleiner.Enabled = true;
	this.panel1.Invalidate(true);
//	MessageBox.Show("PictureBox.height:"+this.pictureBox1.Height.ToString()+" / Panel.height:"+this.panel1.Height.ToString());
	this.pictureBox1.Image = Image.FromFile(FileList[this.selected_image].ToString());
}

zoom ist ein globales double-Array mit Prozent-Werten.
Code:
double[] zoom = {0.06,0.08,0.12,0.17,0.25,0.33,0.50,0.67,1,2,3,4,5,6,7,8,12,16};
zoomAktuell ist auch global und hat beim Laden des Images den Wert -1.
Beim Laden des Images(andere Methode) verkleinere ich die PictureBox entprechend des Panels, so dass immer das ganze Bild zu sehen ist.
Gibt die erste if-Klausel
Code:
if(this.zoomAktuell == -1)
true zurück, habe ich noch keine Position im zoom-Array und ich berechne die nächstgelegene Position, die am besten dazu passt.
FileList ist eine ArrayList und enthält alle in einem Verzeichnis liegenden absolute Bild-Datei-Pfade.

Wenn ich nun zu groß zoome, erscheinen zum einen diese Exceptions und zum Anderen von Windows, dass die Auslagerungsdatei zu klein ist und dass sie nun vergrößert wird.

hmmmm ....

Asterix :confused:
 
OK, ich hab da erst einmal mehrere Dinge:
  1. Warum vernichtest du das Bild per Dispose(), wenn du es am Schluss der Funktion sowieso wieder anzeigen lässt?
  2. Was genau versuchst du mit der AutoScroll-Eigenschaft zu erreichen?
  3. Die PictureBox biete bereits von Haus aus eine Zoom-Funktionalität, indem du die SizeMode-Eigenschaft auf Zoom festlegst. So sparst du dir das eigene Umrechnen.
  4. Das Problem an der Sache ist, dass die PictureBox bei großen Bilder seltsamerweise Probleme hat, den Speicher korrekt zu verwalten. Besser ist es, statt einer PictureBox ein Panel in Verbindung mit GDI+ zu verwenden und die Sache selbst in die Hand zu nehmen.
Im Folgenden findest du eine modifizierte Version für das Zooming, das allerdings nachwievor bei großen Bildern und großen Zoomfaktoren abstürzen wird:

Code:
void TsZoomGroesserClick(object sender, EventArgs e)
{// Zoom setzen
if(this.zoomAktuell < 0)
{// Prozent berechnen
double prozent = (double)pictureBox1.Width / (double)imageOrgWidth;

// Zoom berechnen
for(int i = 0; i < (zoom.Length - 1); i++)
{// Zoom überprüfen
if(zoom[i] >= prozent)
{i++;
break;
}
}

// Zoom setzen
zoomAktuell = i;
}
else
{// Aktuellen Zoom überprüfen
if(zoomAktuell >= (zoom.Length - 1)) return;

// Zoom erhöhen
zoomAktuell++;
}

// Größe der PictureBox festlegen
pictureBox1.Width = (int)((double)imageOrgWidth * zoom[zoomAktuell]);
pictureBox1.Height = (int)((double)imageOrgHeight * zoom[zoomAktuell]);

// AutoScroll-Funktion ?
// [...]

// Buttons (de-)aktivieren
// [...]
}
Gruß
PhoenixLoe
 
Hi,

erstmal danke für die modifizierte Version.

1. ich Dispose das Bild um
  1. Speicher zusparen
  2. Anzeigefehler zuvermeiden
aber hauptsächlich um Speicher zu sparen.

2. Bei der Zoom-Funktion versuche ich mit der AutoScroll-Funktion immer den Bildausschnitt anzuzeigen, der vor dem zoomen in der Mitte des Bildes war. Also einfach nur ein verschieben des Bildes mit AutoScrollPosition.

3. Was ist der Unterschied zwischen 'StretchImage' und 'Zoom'? Ich sehe keinen. Ich nutze 'StretchImage'.

4. Könntest Du mir einen brauchbaren Anfang für ein Panel und GDI+ geben? Bin da nicht so fitt drin.

Asterix
 
Zurück