[f-spot: 2/40] nuke the old AsyncPixbufLoader, replace by ImageLoader



commit c127a1649a4618e986f1093c309bc031e889f88a
Author: Stephane Delcroix <stephane delcroix org>
Date:   Wed Jun 17 16:09:47 2009 +0200

    nuke the old AsyncPixbufLoader, replace by ImageLoader
    
    the new ImageLoader inherits from PixbufLoader and, as such, is single-usage only. It makes it more easy to use.
    
    some FIXME left: fix the CompletePixbuf () in PhotoImageView, load the thumbnail first, handle orientation in ImageView widget

 src/AsyncPixbufLoader.cs  |  440 -----------------------------------
 src/Core.cs               |    2 +-
 src/ImageLoader.cs        |  557 +++++++++++++++++++++++++++++++++++++++++++++
 src/Makefile.am           |    2 +-
 src/PhotoImageView.cs     |   24 ++-
 src/PhotoLoader.cs        |    1 +
 src/ThumbnailGenerator.cs |    2 +-
 7 files changed, 578 insertions(+), 450 deletions(-)
---
diff --git a/src/Core.cs b/src/Core.cs
index 2bb905b..fa2dcfa 100644
--- a/src/Core.cs
+++ b/src/Core.cs
@@ -273,7 +273,7 @@ namespace FSpot {
 			if (toplevels.Count == 0) {
 				Banshee.Kernel.Scheduler.Dispose ();
 				Core.Database.Dispose ();
-				PixbufLoader.Cleanup ();
+				global::PixbufLoader.Cleanup ();
 				Gtk.Application.Quit ();
 				System.Environment.Exit (0);
 			}
diff --git a/src/ImageLoader.cs b/src/ImageLoader.cs
new file mode 100644
index 0000000..7a9a388
--- /dev/null
+++ b/src/ImageLoader.cs
@@ -0,0 +1,557 @@
+//
+// Fspot.ImageLoader.cs
+//
+// Author(s)
+//	Larry Ewing  <lewing novell com>
+//	Stephane Delcroix  <sdelcroix novell com>
+//
+// This is free softwae. See COPYING fro details
+//
+
+using Gtk;
+using System;
+using System.IO;
+using System.Runtime.Remoting.Messaging;
+using FSpot.Platform;
+using FSpot.Utils;
+
+namespace FSpot {
+	public class AreaPreparedEventArgs : EventArgs
+	{
+		bool reduced_resolution;
+
+		public bool ReducedResolution {
+			get { return reduced_resolution; }
+		}
+	
+		public AreaPreparedEventArgs (bool reduced_resolution) : base ()
+		{
+			this.reduced_resolution = reduced_resolution;
+		}
+	}
+
+	public class AreaUpdatedEventArgs : EventArgs
+	{
+		Gdk.Rectangle area;
+		public Gdk.Rectangle Area { 
+			get { return area; }
+		}
+
+		public AreaUpdatedEventArgs (Gdk.Rectangle area) : base ()
+		{
+			this.area = area;
+		}
+	}
+
+	public class ImageLoader : Gdk.PixbufLoader
+	{
+#region public api
+		public ImageLoader () : base ()
+		{	
+		}
+
+		public void Load (Uri uri)
+		{
+			using (ImageFile image_file = ImageFile.Create (uri)) {
+				image_stream = image_file.PixbufStream ();
+			}
+			image_stream.BeginRead (buffer, 0, count, HandleReadDone, null);
+			loading = true;
+		}
+
+		new public event EventHandler<AreaUpdatedEventArgs> AreaUpdated;
+		new public event EventHandler<AreaPreparedEventArgs> AreaPrepared;
+		public event EventHandler Completed;
+
+		bool loading = false;
+		public bool Loading {
+			get { return loading; }
+		}
+
+		bool prepared = false;
+		public bool Prepared {
+			get { return prepared; }
+		}
+#endregion
+
+#region event handlers
+		protected override void OnAreaPrepared ()
+		{
+			base.OnAreaPrepared ();
+			prepared = true;
+			EventHandler<AreaPreparedEventArgs> eh = AreaPrepared;
+			if (eh != null)
+				eh (this, new AreaPreparedEventArgs (false));
+		}
+
+		protected override void OnAreaUpdated (int x, int y, int width, int height)
+		{
+			base.OnAreaUpdated (x, y, width, height);
+			EventHandler<AreaUpdatedEventArgs> eh = AreaUpdated;
+			if (eh != null)
+				eh (this, new AreaUpdatedEventArgs (new Gdk.Rectangle (x, y, width, height)));
+		}
+
+		protected virtual void OnCompleted ()
+		{
+			loading = false;
+			EventHandler eh = Completed;
+			if (eh != null)
+				eh (this, EventArgs.Empty);
+			Close ();
+		}
+#endregion
+
+#region private stuffs
+		System.IO.Stream image_stream;
+		const int count = 1 << 16;
+
+		byte [] buffer = new byte [count];
+
+		void HandleReadDone (IAsyncResult ar)
+		{
+			int byte_read = image_stream.EndRead (ar);
+			if (byte_read == 0) {
+				image_stream.Close ();
+				OnCompleted ();
+				return;
+			}
+			Gtk.Application.Invoke (this, new ReadDoneEventArgs (byte_read), HandleReadDoneInMainLoop);
+		}
+
+		void HandleReadDoneInMainLoop (object sender, EventArgs e)
+		{
+			ReadDoneEventArgs rdea = e as ReadDoneEventArgs;
+			int byte_read = rdea.ByteRead;
+
+			if (Write (buffer, (ulong)byte_read)) {
+				image_stream.BeginRead (buffer, 0, count, HandleReadDone, null);
+			} else
+				OnWriteError ();
+		}
+
+		class ReadDoneEventArgs : EventArgs
+		{
+			int byte_read;
+			public int ByteRead {
+				get { return byte_read; }
+			}
+
+			public ReadDoneEventArgs (int byte_read) : base ()
+			{
+				this.byte_read = byte_read;
+			}
+
+		}
+
+		void OnWriteError ()
+		{
+			Console.WriteLine ("error while feeding the pixbufloader!");
+		}
+#endregion
+//
+//		StreamWrapper stream;
+//		Gdk.PixbufLoader loader;		
+//		Uri uri;
+//		bool area_prepared = false;
+//		bool done_reading = false;
+//		Gdk.Pixbuf pixbuf;
+//		Gdk.Pixbuf thumb;
+//		PixbufOrientation orientation;
+//
+//		private Gdk.AreaUpdatedHandler au;
+//		private System.EventHandler ap;
+//		private System.EventHandler ev;
+//
+//		byte [] buffer = new byte [1 << 16];
+//
+//		public event EventHandler<AreaUpdatedEventArgs> AreaUpdated;
+//		public event EventHandler<AreaPreparedEventArgs> AreaPrepared;
+//		public event System.EventHandler Done;
+//
+//		// If the photo we just loaded has an out of date
+//		// thumbnail save a new one
+//		bool validate_thumbnail = true;
+//
+//		// Limit pass control back to the main loop after
+//		// chunk_timeout miliseconds.
+//		int  chunk_timeout = 75;
+//
+//		Delay delay;
+//		IAsyncResult result;
+//
+//		Gdk.Rectangle damage;
+//
+//		public AsyncPixbufLoader ()
+//		{
+//			delay = new Delay (0, new GLib.IdleHandler (AsyncRead));
+//			ap = new System.EventHandler (HandleAreaPrepared);
+//			au = new Gdk.AreaUpdatedHandler (HandleAreaUpdated);
+//			ev = new System.EventHandler (HandleClosed);
+//		}
+//		
+//		public bool Loading
+//		{
+//			get { return ! done_reading; }
+//		}
+//
+//		public bool Prepared
+//		{
+//			get { return area_prepared; }
+//		}
+//
+//		public Gdk.Pixbuf Pixbuf {
+//			get { return pixbuf; }
+//		}
+//
+//		public Gdk.PixbufLoader Loader {
+//			get { return loader; }
+//		}
+//		
+//		private void FileLoad (ImageFile img)
+//		{
+//			pixbuf = img.Load ();
+//			done_reading = true;
+//			if (Done != null)
+//				Done (this, System.EventArgs.Empty);
+//		}
+//
+//		public void Load (Uri uri)
+//		{
+//			this.uri = uri;
+//
+//			delay.Stop ();
+//
+//			if (!done_reading)
+//				Close ();
+//
+//			done_reading = false;
+//			area_prepared = false;
+//			damage = Gdk.Rectangle.Zero;
+//
+//			using (ImageFile img = ImageFile.Create (uri)) {
+//				orientation = Accelerometer.GetViewOrientation (img.Orientation);
+//			
+//				try {
+//					PixbufOrientation thumb_orientation = Accelerometer.GetViewOrientation (PixbufOrientation.TopLeft);
+//					thumb = ThumbnailFactory.LoadThumbnail (uri);
+//					thumb = PixbufUtils.TransformOrientation (thumb, thumb_orientation);
+//					
+//					if (FSpot.ColorManagement.IsEnabled && !thumb.HasAlpha) {
+//						if (img.GetProfile () == null)
+//							FSpot.ColorManagement.PhotoImageView.Transform = FSpot.ColorManagement.StandardTransform ();
+//						else
+//							FSpot.ColorManagement.PhotoImageView.Transform = FSpot.ColorManagement.CreateTransform (thumb, img.GetProfile ());
+//					}
+//					else
+//						FSpot.ColorManagement.PhotoImageView.Transform = null;
+//				} catch (System.Exception e) {
+//					if (!(e is GLib.GException)) 
+//						Log.DebugException (e);
+//				}
+//
+//				System.IO.Stream nstream = img.PixbufStream ();
+//				if (nstream == null) {
+//					FileLoad (img);
+//					return;
+//				} else
+//					stream = new StreamWrapper (nstream);
+//
+//				loader = new Gdk.PixbufLoader ();
+//				loader.AreaPrepared += ap;
+//				loader.AreaUpdated += au;
+//				loader.Closed += ev;
+//
+//				if (AreaPrepared != null && thumb != null) {
+//					pixbuf = thumb;
+//					AreaPrepared (this, new AreaPreparedEventArgs (true));
+//				}
+//
+//				ThumbnailGenerator.Default.PushBlock ();
+//				//AsyncIORead (null);
+//				if (nstream is IOChannel) {
+//					((IOChannel)nstream).DataReady += IOChannelRead;
+//				} else
+//					delay.Start ();
+//			}
+//
+//		}			
+//
+//		private void LoadToAreaPrepared ()
+//		{
+//			while (!area_prepared && delay.IsPending)
+//				Application.RunIteration ();
+//		}
+//
+//		public void LoadToDone ()
+//		{
+//			while (!done_reading && delay.IsPending)
+//				Application.RunIteration ();
+//		}
+//
+//		private void Close () {
+//			ThumbnailGenerator.Default.PopBlock ();
+//				
+//			try {
+//				result = null;
+//
+//				delay.Stop ();
+//				if (loader != null) { 
+//					loader.AreaPrepared -= ap;
+//					loader.AreaUpdated -= au;
+//					// this can throw exceptions
+//					loader.Close ();
+//				}
+//			} catch (System.Exception) {
+//				//System.Console.WriteLine (e.ToString ());
+//				if (pixbuf != null)
+//					pixbuf.Dispose ();
+//
+//				pixbuf = null;
+//			} finally {
+//				if (loader != null) {
+//					loader.Closed -= ev;
+//					loader.Dispose ();
+//				}
+//
+//				loader = null;
+//
+//				if (stream != null) 
+//					stream.Close ();
+//
+//				stream = null;
+//			}
+//		}
+//
+//		private void UpdateListeners ()
+//		{
+//			Gdk.Rectangle area = damage;
+//			
+//			if (pixbuf != null && loader.Pixbuf != null && loader.Pixbuf != pixbuf && damage != Gdk.Rectangle.Zero)
+//				area = PixbufUtils.TransformAndCopy (loader.Pixbuf, pixbuf, orientation, damage);
+//			
+//			if (area.Width != 0 && area.Height != 0 && AreaUpdated != null)
+//				AreaUpdated (this, new AreaUpdatedEventArgs (area));
+//
+//			//System.Console.WriteLine ("orig {0} tform {1}", damage.ToString (), area.ToString ());
+//			damage = Gdk.Rectangle.Zero;
+//		}
+//
+//		private class StreamWrapper {
+//			private delegate int ReadDelegate (byte [] buffer, int offset, int count);
+//			System.IO.Stream stream;
+//
+//			public System.IO.Stream Stream {
+//				get { return stream; }
+//			}
+//
+//			public StreamWrapper (System.IO.Stream stream)
+//			{
+//				this.stream = stream;
+//			}
+//
+//			public int Read (byte[] buffer, int offset, int count)
+//			{
+//				return stream.Read (buffer, offset, count);
+//			}
+//
+//			public IAsyncResult BeginRead (byte [] buffer, int offset, int count, AsyncCallback cb, object state)
+//			{
+//				ReadDelegate del = new ReadDelegate (Read);
+//				return del.BeginInvoke (buffer, offset, count, cb, state);
+//			}
+//			
+//			public int EndRead (IAsyncResult result)
+//			{
+//				AsyncResult art = result as AsyncResult;
+//				ReadDelegate del = art.AsyncDelegate as ReadDelegate;
+//				int i = del.EndInvoke (result);
+//				return i;
+//			}
+//			
+//			public void Close ()
+//			{
+//				stream.Close ();
+//			}
+//		}
+//
+//		private void IOChannelRead (object sender, DataReadEventArgs args)
+//		{
+//			//Console.WriteLine ("IO read {0}", args.Condition);
+//
+//			if ( (System.IO.Stream)sender == stream.Stream)				
+//				args.Continue = AsyncRead ();
+//			else {
+//				args.Continue = false;
+//				stream.Close ();
+//			}
+//
+//			return;
+//		}
+//
+//		private bool AsyncIORead ()
+//		{
+//			try {
+//				if (result == null) {
+//					//System.Console.WriteLine ("start read");
+//					result = stream.BeginRead (buffer, 0, buffer.Length, AsyncIOEnd, stream);
+//				} else {
+//					//Console.WriteLine ("not done");
+//					UpdateListeners ();
+//				}
+//			} catch (System.Exception e) {
+//				System.Console.WriteLine ("In read got {0}", e);
+//			}
+//			
+//			if (done_reading)
+//				delay.Stop ();
+//
+//			return ! done_reading;
+//		}
+//
+//		private void AsyncIOEnd (IAsyncResult iar)
+//		{
+//			//System.Console.WriteLine ("ioended");
+//			if (stream == (StreamWrapper)iar.AsyncState)
+//				Gtk.Application.Invoke (ReadDone);
+//		}
+//		
+//		public void ReadDone (object sender, System.EventArgs args)
+//		{
+//			if (result == null)
+//				return;
+//
+//			int len = 0;
+//			try {
+//				len = stream.EndRead (result);
+//				//System.Console.WriteLine ("read {0} bytes", len);
+//				loader.Write (buffer, (ulong)len);
+//			} catch (System.ObjectDisposedException od) {
+//				System.Console.WriteLine ("error in endread {0}", od);
+//				//delay.Start ();
+//				len = -1;
+//			} catch (GLib.GException e) {
+//				System.Console.WriteLine (e.ToString ());
+//				pixbuf = null;
+//				len = -1;
+//			}
+//			result = null;
+//
+//			if (len <= 0) {
+//				if (loader.Pixbuf == null) {
+//					if (pixbuf != null)
+//						pixbuf.Dispose ();
+//					
+//					pixbuf = null;
+//				}
+//				
+//				UpdateListeners ();
+//				done_reading = true;
+//				Close ();
+//				return;
+//			}
+//		}
+//	
+//		private bool AsyncRead () 
+//		{
+//#if false
+//			return AsyncIORead ();
+//#else	
+//			return NormalRead ();
+//#endif		
+//		}
+//
+//		private bool NormalRead ()
+//		{
+//			System.DateTime start_time = System.DateTime.Now;
+//			System.TimeSpan span = start_time - start_time;
+//
+//			do {
+//				span = System.DateTime.Now - start_time;
+//
+//				int len;
+//				try {
+//					len = stream.Read (buffer, 0, buffer.Length);
+//					loader.Write (buffer, (ulong)len);
+//				} catch (Exception) {
+//					len = -1;
+//				}
+//
+//				if (len <= 0) {
+//					if (loader.Pixbuf == null) {
+//						if (pixbuf != null)
+//							pixbuf.Dispose ();
+//
+//						pixbuf = null;
+//					}
+//
+//					UpdateListeners ();
+//					done_reading = true;
+//					Close ();
+//					return false;
+//				}
+//			} while (!done_reading && span.TotalMilliseconds <= chunk_timeout);
+//
+//			UpdateListeners ();
+//			return true;
+//		}
+//		
+//		private void HandleAreaPrepared (object sender, System.EventArgs args)
+//		{
+//			pixbuf = PixbufUtils.TransformOrientation (loader.Pixbuf, orientation, false);
+//
+//			if (thumb != null && pixbuf != null)
+//				thumb.Composite (pixbuf, 0, 0,
+//						 pixbuf.Width, pixbuf.Height,
+//						 0.0, 0.0,
+//						 pixbuf.Width/(double)thumb.Width, pixbuf.Height/(double)thumb.Height,
+//						 Gdk.InterpType.Bilinear, 0xff);
+//			
+//			if (thumb != null)
+//				if (!ThumbnailFactory.ThumbnailIsValid (thumb, uri))
+//					FSpot.ThumbnailGenerator.Default.Request (uri, 0, 256, 256);
+//
+//			area_prepared = true;			
+//			if (AreaUpdated != null)
+//				AreaPrepared (this, new AreaPreparedEventArgs (false));
+//
+//			if (thumb != null)
+//				thumb.Dispose ();
+//			thumb = null;
+//		}
+//
+//		private void HandleAreaUpdated (object sender, Gdk.AreaUpdatedArgs args)
+//		{
+//			Gdk.Rectangle area = new Gdk.Rectangle (args.X, args.Y, args.Width, args.Height);
+//			
+//			if (damage.Width == 0 || damage.Height == 0)
+//				damage = area;
+//			else 
+//				damage = area.Union (damage);
+//		}
+//
+//		private void HandleClosed (object sender, System.EventArgs args) 
+//		{
+//			// FIXME This should probably queue the
+//			// thumbnail regeneration to a worker thread
+//			if (validate_thumbnail && done_reading && pixbuf != null) {
+//				PhotoLoader.ValidateThumbnail (uri, pixbuf);
+//			}
+//
+//			if (Done != null)
+//				Done (this, System.EventArgs.Empty);
+//			
+//			ThumbnailGenerator.Default.PopBlock ();
+//		}
+//
+//		public void Dispose ()
+//		{
+//			Close ();
+//
+//			if (pixbuf != null)
+//				pixbuf.Dispose ();
+//		}
+	}
+}
+
+	       
diff --git a/src/Makefile.am b/src/Makefile.am
index 5b41601..11fbcd4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -107,7 +107,6 @@ NULL_PLATFORM_CSDISTFILES =				\
 	$(srcdir)/Platform/Null/WebProxy.cs
 
 F_SPOT_CSDISTFILES =				\
-	$(srcdir)/AsyncPixbufLoader.cs		\
 	$(srcdir)/BlockProcessor.cs		\
 	$(srcdir)/BitConverter.cs		\
 	$(srcdir)/PhotoArray.cs 		\
@@ -170,6 +169,7 @@ F_SPOT_CSDISTFILES =				\
 	$(srcdir)/GroupSelector.cs		\
 	$(srcdir)/Accelerometer.cs		\
 	$(srcdir)/Histogram.cs			\
+	$(srcdir)/ImageLoader.cs		\
 	$(srcdir)/ImportBackend.cs		\
 	$(srcdir)/ImportCommand.cs		\
 	$(srcdir)/InfoOverlay.cs		\
diff --git a/src/PhotoImageView.cs b/src/PhotoImageView.cs
index 4a06136..0df8535 100644
--- a/src/PhotoImageView.cs
+++ b/src/PhotoImageView.cs
@@ -37,12 +37,13 @@ namespace FSpot.Widgets {
 			FSpot.ColorManagement.PhotoImageView = this;
 		}
 
+//		ImageLoader loader;
 		public PhotoImageView (BrowsablePointer item)
 		{
-			loader = new FSpot.AsyncPixbufLoader ();
-			loader.AreaUpdated += HandlePixbufAreaUpdated;
-			loader.AreaPrepared += HandlePixbufPrepared;
-			loader.Done += HandleDone;
+//			loader = new ImageLoader ();
+//			loader.AreaUpdated += HandlePixbufAreaUpdated;
+//			loader.AreaPrepared += HandlePixbufPrepared;
+//			loader.Completed += HandleDone;
 			
 			FSpot.ColorManagement.PhotoImageView = this;
 			Transform = FSpot.ColorManagement.StandardTransform (); //for preview windows
@@ -74,7 +75,9 @@ namespace FSpot.Widgets {
 
 		public Gdk.Pixbuf CompletePixbuf ()
 		{
-			loader.LoadToDone ();
+			//FIXME: this should be an async call
+//			while (loader.Loading)
+//				Gtk.Application.RunIteration ();
 			return this.Pixbuf;
 		}
 
@@ -131,6 +134,7 @@ namespace FSpot.Widgets {
 
 		private void HandlePixbufPrepared (object sender, AreaPreparedEventArgs args)
 		{
+			ImageLoader loader = sender as ImageLoader;
 			if (!ShowProgress)
 				return;
 
@@ -146,6 +150,8 @@ namespace FSpot.Widgets {
 
 		private void HandleDone (object sender, System.EventArgs args)
 		{
+Log.Warning ("PhotoImageView: loading DONE");
+			ImageLoader loader = sender as ImageLoader;
 			// FIXME the error hander here needs to provide proper information and we should
 			// pass the state and the write exception in the args
 			Gdk.Pixbuf prev = this.Pixbuf;
@@ -206,7 +212,6 @@ namespace FSpot.Widgets {
 			}
 		}
 		
-		FSpot.AsyncPixbufLoader loader;
 
 		private void LoadErrorImage (System.Exception e)
 		{
@@ -254,6 +259,11 @@ namespace FSpot.Widgets {
 				try {
 					if (Item.IsValid) {
 						System.Uri uri = Item.Current.DefaultVersionUri;
+Log.Warning ("PhotoImageView: Loader.Load");
+						ImageLoader loader = new ImageLoader ();
+						loader.AreaUpdated += HandlePixbufAreaUpdated;
+						loader.AreaPrepared += HandlePixbufPrepared;
+						loader.Completed += HandleDone;
 						loader.Load (uri);
 					} else
 						LoadErrorImage (null);
@@ -391,7 +401,7 @@ namespace FSpot.Widgets {
 		{
 			//loader.AreaUpdated -= HandlePixbufAreaUpdated;
 			//loader.AreaPrepared -= HandlePixbufPrepared;
-			loader.Dispose ();
+			//loader.Dispose ();
 		}
 	}
 }
diff --git a/src/PhotoLoader.cs b/src/PhotoLoader.cs
index 34d0e1b..9ccb62e 100644
--- a/src/PhotoLoader.cs
+++ b/src/PhotoLoader.cs
@@ -4,6 +4,7 @@ using FSpot.Platform;
 using FSpot.Utils;
 
 namespace FSpot {
+	[Obsolete ("nuke or rename this")]
 	public class PhotoLoader {
 		public PhotoQuery query;
 
diff --git a/src/ThumbnailGenerator.cs b/src/ThumbnailGenerator.cs
index 12d15a5..9e9b127 100644
--- a/src/ThumbnailGenerator.cs
+++ b/src/ThumbnailGenerator.cs
@@ -13,7 +13,7 @@ using FSpot.Utils;
 using FSpot.Platform;
 
 namespace FSpot {
-	public class ThumbnailGenerator : PixbufLoader {
+	public class ThumbnailGenerator : global::PixbufLoader {
 
 		static public ThumbnailGenerator Default = new ThumbnailGenerator ();
 		



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]