[f-spot/rubenv-gsoc-2009: 23/86] Transform the simple ImageLoaders to item loaders.
- From: Ruben Vermeersch <rubenv src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [f-spot/rubenv-gsoc-2009: 23/86] Transform the simple ImageLoaders to item loaders.
- Date: Sun, 23 May 2010 12:33:53 +0000 (UTC)
commit b7e907e9507ed51ef11112b07bdfbed0d78d1937
Author: Ruben Vermeersch <ruben savanne be>
Date: Mon Jul 27 20:10:57 2009 +0200
Transform the simple ImageLoaders to item loaders.
This brings back the ideas from the unified-loading branch.
Changes:
* Rename load_thumbnail to the more suitable load_embedded in
libfspotraw.
* Changed IImageLoader to expose multiple loadable items. Related
changes to event args, extension methods etc.
* Ported both loaders to this new API. Lots of code duplication because
they have no shared base class. Not very fond of this fact.
* Changed PhotoImageView such that it uses the new ImageLoader API.
lib/libfspotraw/fspot-librawloader.cpp | 2 +-
lib/libfspotraw/fspot-librawloader.h | 2 +-
src/Loaders/AreaPreparedEventArgs.cs | 17 +--
src/Loaders/AreaUpdatedEventArgs.cs | 19 +-
src/Loaders/GdkImageLoader.cs | 290 +++++++++++++++++++-----------
src/Loaders/IImageLoader.cs | 12 +-
src/Loaders/IImageLoaderExtensions.cs | 54 ++++++
src/Loaders/ImageLoader.cs | 2 +-
src/Loaders/ImageLoaderItem.cs | 21 +++
src/Loaders/ImageLoaderItemExtensions.cs | 30 +++
src/Loaders/ItemCompletedEventArgs.cs | 22 +++
src/Loaders/LibrawImageLoader.cs | 274 ++++++++++++++++++++++------
src/Makefile.am | 4 +
src/PhotoImageView.cs | 30 +++-
14 files changed, 579 insertions(+), 200 deletions(-)
---
diff --git a/lib/libfspotraw/fspot-librawloader.cpp b/lib/libfspotraw/fspot-librawloader.cpp
index 0df9dff..0a96205 100644
--- a/lib/libfspotraw/fspot-librawloader.cpp
+++ b/lib/libfspotraw/fspot-librawloader.cpp
@@ -180,7 +180,7 @@ fspot_librawloader_finalize (GObject *object)
}
GdkPixbuf *
-fspot_librawloader_load_thumbnail (FSpotLibrawLoader *self, int *orientation)
+fspot_librawloader_load_embedded (FSpotLibrawLoader *self, int *orientation)
{
int result;
libraw_processed_image_t *image = NULL;
diff --git a/lib/libfspotraw/fspot-librawloader.h b/lib/libfspotraw/fspot-librawloader.h
index 5e25484..4b155d5 100644
--- a/lib/libfspotraw/fspot-librawloader.h
+++ b/lib/libfspotraw/fspot-librawloader.h
@@ -42,7 +42,7 @@ struct _FSpotLibrawLoaderClass
};
GType fspot_librawloader_get_type (void);
-GdkPixbuf * fspot_librawloader_load_thumbnail (FSpotLibrawLoader *self, int *orientation);
+GdkPixbuf * fspot_librawloader_load_embedded (FSpotLibrawLoader *self, int *orientation);
GdkPixbuf * fspot_librawloader_load_full (FSpotLibrawLoader *self);
FSpotLibrawLoader * fspot_librawloader_new (const gchar *filename);
gboolean fspot_librawloader_get_aborted (FSpotLibrawLoader *self);
diff --git a/src/Loaders/AreaPreparedEventArgs.cs b/src/Loaders/AreaPreparedEventArgs.cs
index 82387d7..8055a15 100644
--- a/src/Loaders/AreaPreparedEventArgs.cs
+++ b/src/Loaders/AreaPreparedEventArgs.cs
@@ -1,29 +1,22 @@
//
-// Fspot.Loaders.AreaPreparedEventArgs.cs
-//
-// Copyright (c) 2009 Novell, Inc.
+// Fspot/Loaders/AreaPreparedEventArgs.cs
//
// Author(s)
-// Stephane Delcroix <sdelcroix novell com>
+// Ruben Vermeersch <ruben savanne be>
//
// This is free software. See COPYING for details
//
using System;
-using Gdk;
namespace FSpot.Loaders {
public class AreaPreparedEventArgs : EventArgs
{
- bool reduced_resolution;
+ public ImageLoaderItem Item { get; private set; }
- public bool ReducedResolution {
- get { return reduced_resolution; }
- }
-
- public AreaPreparedEventArgs (bool reduced_resolution) : base ()
+ public AreaPreparedEventArgs (ImageLoaderItem item) : base ()
{
- this.reduced_resolution = reduced_resolution;
+ this.Item = item;
}
}
}
diff --git a/src/Loaders/AreaUpdatedEventArgs.cs b/src/Loaders/AreaUpdatedEventArgs.cs
index 7308edf..17dd5cb 100644
--- a/src/Loaders/AreaUpdatedEventArgs.cs
+++ b/src/Loaders/AreaUpdatedEventArgs.cs
@@ -1,28 +1,27 @@
//
-// Fspot.Loaders.AreaUpdatedEventArgs.cs
-//
-// Copyright (c) 2009 Novell, Inc.
+// Fspot/Loaders/AreaUpdatedEventArgs.cs
//
// Author(s)
+// Ruben Vermeersch <ruben savanne be>
// Stephane Delcroix <sdelcroix novell com>
//
+//
// This is free software. See COPYING for details
//
-using System;
using Gdk;
+using System;
namespace FSpot.Loaders {
public class AreaUpdatedEventArgs : EventArgs
{
- Gdk.Rectangle area;
- public Gdk.Rectangle Area {
- get { return area; }
- }
+ public ImageLoaderItem Item { get; private set; }
+ public Rectangle Area { get; private set; }
- public AreaUpdatedEventArgs (Gdk.Rectangle area) : base ()
+ public AreaUpdatedEventArgs (ImageLoaderItem item, Rectangle area) : base ()
{
- this.area = area;
+ this.Item = item;
+ this.Area = area;
}
}
}
diff --git a/src/Loaders/GdkImageLoader.cs b/src/Loaders/GdkImageLoader.cs
index 4d9a24c..8812279 100644
--- a/src/Loaders/GdkImageLoader.cs
+++ b/src/Loaders/GdkImageLoader.cs
@@ -9,86 +9,69 @@
// This is free software. See COPYING for details
//
+using FSpot.Platform;
+using FSpot.Utils;
+using Gdk;
using System;
using System.Threading;
-using Gdk;
-using FSpot.Utils;
-using FSpot.Platform;
namespace FSpot.Loaders {
public class GdkImageLoader : Gdk.PixbufLoader, IImageLoader
{
- Uri uri = null;
-
-#region public api
- public GdkImageLoader () : base ()
- {
- }
-
- public void Load (Uri uri)
- {
- if (this.uri != null)
- throw new Exception ("You should only request one image per loader!");
- this.uri = uri;
-
- if (is_disposed)
- return;
+ Uri uri;
+ object sync_handle = new object ();
+ bool is_disposed = false;
+ Rectangle damage;
- //First, send a thumbnail if we have one
- if ((thumb = ThumbnailFactory.LoadThumbnail (uri)) != null) {
- pixbuf_orientation = PixbufOrientation.TopLeft;
- EventHandler<AreaPreparedEventArgs> prep = AreaPrepared;
- if (prep != null)
- prep (this, new AreaPreparedEventArgs (true));
- EventHandler<AreaUpdatedEventArgs> upd = AreaUpdated;
- if (upd != null)
- upd (this, new AreaUpdatedEventArgs (new Rectangle (0, 0, thumb.Width, thumb.Height)));
- }
+ public ImageLoaderItem ItemsRequested { get; private set; }
+ public ImageLoaderItem ItemsCompleted { get; private set; }
- using (ImageFile image_file = ImageFile.Create (uri)) {
- image_stream = image_file.PixbufStream ();
- pixbuf_orientation = image_file.Orientation;
- }
+ Pixbuf thumbnail;
+ public Pixbuf Thumbnail {
+ get { return PixbufUtils.ShallowCopy (thumbnail); }
+ private set { thumbnail = value; }
+ }
+ public PixbufOrientation ThumbnailOrientation { get; private set; }
- loading = true;
- // The ThreadPool.QueueUserWorkItem hack is there cause, as the bytes to read are present in the stream,
- // the Read is CompletedAsynchronously, blocking the mainloop
- image_stream.BeginRead (buffer, 0, count, delegate (IAsyncResult r) {
- ThreadPool.QueueUserWorkItem (delegate {HandleReadDone (r);});
- }, null);
+ public Pixbuf Large {
+ get { return PixbufUtils.ShallowCopy (Pixbuf); }
}
+ public PixbufOrientation LargeOrientation { get; private set; }
+
+ public Pixbuf Full { get { return Large; } }
+ public PixbufOrientation FullOrientation { get { return LargeOrientation; } }
new public event EventHandler<AreaPreparedEventArgs> AreaPrepared;
new public event EventHandler<AreaUpdatedEventArgs> AreaUpdated;
- public event EventHandler Completed;
+ public event EventHandler<ItemsCompletedEventArgs> Completed;
+ public bool Loading { get; private set; }
- Pixbuf thumb;
- new public Pixbuf Pixbuf {
- get {
- if (thumb != null)
- return thumb;
- return base.Pixbuf;
- }
- }
+#region public api
+ public GdkImageLoader (Uri uri) : base ()
+ {
+ this.uri = uri;
+ Loading = false;
- bool loading = false;
- public bool Loading {
- get { return loading; }
+ ItemsRequested = ImageLoaderItem.None;
+ ItemsCompleted = ImageLoaderItem.None;
}
- bool notify_prepared = false;
- bool prepared = false;
- public bool Prepared {
- get { return prepared; }
- }
+ public ImageLoaderItem Load (ImageLoaderItem items, bool async)
+ {
+ if (is_disposed)
+ return ImageLoaderItem.None;
+
+ ItemsRequested |= items;
- PixbufOrientation pixbuf_orientation = PixbufOrientation.TopLeft;
- public PixbufOrientation PixbufOrientation {
- get { return pixbuf_orientation; }
+ StartLoading ();
+
+ if (!async)
+ WaitForCompletion (items);
+
+ return ItemsCompleted & items;
}
- bool is_disposed = false;
public override void Dispose ()
{
is_disposed = true;
@@ -99,9 +82,9 @@ namespace FSpot.Loaders {
{
}
Close ();
- if (thumb != null) {
- thumb.Dispose ();
- thumb = null;
+ if (thumbnail != null) {
+ thumbnail.Dispose ();
+ thumbnail = null;
}
base.Dispose ();
}
@@ -120,9 +103,8 @@ namespace FSpot.Loaders {
if (is_disposed)
return;
- prepared = notify_prepared = true;
- damage = Rectangle.Zero;
base.OnAreaPrepared ();
+ SignalAreaPrepared (ImageLoaderItem.Large | ImageLoaderItem.Full);
}
protected override void OnAreaUpdated (int x, int y, int width, int height)
@@ -131,8 +113,8 @@ namespace FSpot.Loaders {
return;
Rectangle area = new Rectangle (x, y, width, height);
- damage = damage == Rectangle.Zero ? area : damage.Union (area);
base.OnAreaUpdated (x, y, width, height);
+ SignalAreaUpdated (ImageLoaderItem.Large | ImageLoaderItem.Full, area);
}
protected virtual void OnCompleted ()
@@ -140,10 +122,7 @@ namespace FSpot.Loaders {
if (is_disposed)
return;
- EventHandler eh = Completed;
- if (eh != null)
- eh (this, EventArgs.Empty);
- Close ();
+ SignalItemCompleted (ImageLoaderItem.Large | ImageLoaderItem.Full);
}
#endregion
@@ -151,62 +130,163 @@ namespace FSpot.Loaders {
System.IO.Stream image_stream;
const int count = 1 << 16;
byte [] buffer = new byte [count];
- bool notify_completed = false;
- Rectangle damage;
- object sync_handle = new object ();
- void HandleReadDone (IAsyncResult ar)
+ void StartLoading ()
+ {
+ lock (sync_handle) {
+ if (Loading)
+ return;
+ Loading = true;
+ }
+
+ // Load thumbnail immediately, if required
+ if (!ItemsCompleted.Contains (ImageLoaderItem.Thumbnail) &&
+ ItemsRequested.Contains (ImageLoaderItem.Thumbnail)) {
+ LoadThumbnail ();
+ }
+
+ ThreadPool.QueueUserWorkItem (delegate {
+ try {
+ DoLoad ();
+ } catch (Exception e) {
+ Log.Debug (e.ToString ());
+ Log.Debug ("Requested: {0}, Done: {1}", ItemsRequested, ItemsCompleted);
+ Gtk.Application.Invoke (delegate { throw e; });
+ }
+ });
+ }
+
+ void DoLoad ()
+ {
+ while (!is_disposed && !ItemsCompleted.Contains (ItemsRequested)) {
+ if (ItemsRequested.Contains (ImageLoaderItem.Thumbnail))
+ LoadThumbnail ();
+
+ if (ItemsRequested.Contains (ImageLoaderItem.Large))
+ LoadLarge ();
+ }
+
+ lock (sync_handle) {
+ Loading = false;
+ }
+ }
+
+ void LoadThumbnail ()
{
if (is_disposed)
return;
- int byte_read = image_stream.EndRead (ar);
- lock (sync_handle) {
+ // Check if the thumbnail exists, if not: try to create it from the
+ // Large image. Will request Large if it is not present and wait
+ // for the next call to generate it (see the loop in DoLoad).
+ if (!ThumbnailFactory.ThumbnailExists (uri)) {
+ if (ItemsCompleted.Contains (ImageLoaderItem.Large)) {
+ ThumbnailFactory.SaveThumbnail (Pixbuf, uri);
+ } else {
+ ItemsRequested |= ImageLoaderItem.Large;
+ return;
+ }
+ }
+
+ Thumbnail = ThumbnailFactory.LoadThumbnail (uri);
+ ThumbnailOrientation = PixbufOrientation.TopLeft;
+ if (Thumbnail == null)
+ throw new Exception ("Null thumbnail returned");
+
+ SignalAreaPrepared (ImageLoaderItem.Thumbnail);
+ SignalAreaUpdated (ImageLoaderItem.Thumbnail, new Rectangle (0, 0, thumbnail.Width, thumbnail.Height));
+ SignalItemCompleted (ImageLoaderItem.Thumbnail);
+ }
+
+ void LoadLarge ()
+ {
+ if (is_disposed)
+ return;
+
+ using (ImageFile image_file = ImageFile.Create (uri)) {
+ image_stream = image_file.PixbufStream ();
+ LargeOrientation = image_file.Orientation;
+ }
+
+ while (Loading && !is_disposed) {
+ int byte_read = image_stream.Read (buffer, 0, count);
+
if (byte_read == 0) {
image_stream.Close ();
- Close ();
- loading = false;
- notify_completed = true;
+ Close ();
+ Loading = false;
+ SignalItemCompleted (ImageLoaderItem.Large | ImageLoaderItem.Full);
} else {
try {
- if (!is_disposed && Write (buffer, (ulong)byte_read))
- image_stream.BeginRead (buffer, 0, count, HandleReadDone, null);
+ Write (buffer, (ulong)byte_read);
} catch (System.ObjectDisposedException) {
} catch (GLib.GException) {
}
}
}
+ }
- GLib.Idle.Add (delegate {
- //Send the AreaPrepared event
- if (notify_prepared) {
- notify_prepared = false;
- if (thumb != null) {
- thumb.Dispose ();
- thumb = null;
- }
+ void WaitForCompletion (ImageLoaderItem items)
+ {
+ while (!ItemsCompleted.Contains(items)) {
+ Log.Debug ("Waiting for completion of {0} (done: {1})", ItemsRequested, ItemsCompleted);
+ Monitor.Enter (sync_handle);
+ Monitor.Wait (sync_handle);
+ Monitor.Exit (sync_handle);
+ Log.Debug ("Woke up after waiting for {0} (done: {1})", ItemsRequested, ItemsCompleted);
+ }
+ }
- EventHandler<AreaPreparedEventArgs> eh = AreaPrepared;
- if (eh != null)
- eh (this, new AreaPreparedEventArgs (false));
- }
+ void SignalAreaPrepared (ImageLoaderItem item) {
+ damage = Rectangle.Zero;
+ EventHandler<AreaPreparedEventArgs> eh = AreaPrepared;
+ if (eh != null)
+ GLib.Idle.Add (delegate {
+ eh (this, new AreaPreparedEventArgs (item));
+ return false;
+ });
+ }
- //Send the AreaUpdated events
- if (damage != Rectangle.Zero) {
- EventHandler<AreaUpdatedEventArgs> eh = AreaUpdated;
- if (eh != null)
- eh (this, new AreaUpdatedEventArgs (damage));
- damage = Rectangle.Zero;
- }
+ void SignalAreaUpdated (ImageLoaderItem item, Rectangle area) {
+ EventHandler<AreaUpdatedEventArgs> eh = AreaUpdated;
+ if (eh == null)
+ return;
- //Send the Completed event
- if (notify_completed) {
- notify_completed = false;
- OnCompleted ();
+ lock (sync_handle) {
+ if (damage == Rectangle.Zero) {
+ damage = area;
+ GLib.Idle.Add (delegate {
+ Rectangle to_signal;
+ lock (sync_handle) {
+ to_signal = damage;
+ damage = Rectangle.Zero;
+ }
+ eh (this, new AreaUpdatedEventArgs (item, to_signal));
+ return false;
+ });
+ } else {
+ damage = damage.Union (area);
}
+ }
+ }
+
+ void SignalItemCompleted (ImageLoaderItem item)
+ {
+ ItemsCompleted |= item;
+ Log.Debug ("Notifying completion of {0} (done: {1}, requested: {2})", item, ItemsCompleted, ItemsRequested);
- return false;
- });
+ Monitor.Enter (sync_handle);
+ Monitor.PulseAll (sync_handle);
+ Monitor.Exit (sync_handle);
+
+ Log.Debug ("Signalled!");
+
+ EventHandler<ItemsCompletedEventArgs> eh = Completed;
+ if (eh != null)
+ GLib.Idle.Add (delegate {
+ eh (this, new ItemsCompletedEventArgs (item));
+ return false;
+ });
}
#endregion
}
diff --git a/src/Loaders/IImageLoader.cs b/src/Loaders/IImageLoader.cs
index 84c7408..1894a5a 100644
--- a/src/Loaders/IImageLoader.cs
+++ b/src/Loaders/IImageLoader.cs
@@ -19,11 +19,15 @@ namespace FSpot.Loaders {
event EventHandler<AreaPreparedEventArgs> AreaPrepared;
event EventHandler<AreaUpdatedEventArgs> AreaUpdated;
- event EventHandler Completed;
+ event EventHandler<ItemsCompletedEventArgs> Completed;
- void Load (Uri uri);
+ ImageLoaderItem Load (ImageLoaderItem items, bool async);
- Pixbuf Pixbuf { get; }
- PixbufOrientation PixbufOrientation { get; }
+ Pixbuf Thumbnail { get; }
+ PixbufOrientation ThumbnailOrientation { get; }
+ Pixbuf Large { get; }
+ PixbufOrientation LargeOrientation { get; }
+ Pixbuf Full { get; }
+ PixbufOrientation FullOrientation { get; }
}
}
diff --git a/src/Loaders/IImageLoaderExtensions.cs b/src/Loaders/IImageLoaderExtensions.cs
new file mode 100644
index 0000000..d7b39b8
--- /dev/null
+++ b/src/Loaders/IImageLoaderExtensions.cs
@@ -0,0 +1,54 @@
+//
+// Fspot/Loaders/IImageLoaderExtensions.cs
+//
+// Author(s)
+// Ruben Vermeersch <ruben savanne be>
+//
+// This is free software. See COPYING for details
+//
+
+using Gdk;
+using System;
+using FSpot.Utils;
+
+namespace FSpot.Loaders {
+ public static class IImageLoaderExtensions {
+ // Async loading
+ public static void Load (this IImageLoader loader, ImageLoaderItem items, EventHandler<ItemsCompletedEventArgs> handler)
+ {
+ loader.Completed += handler;
+ ImageLoaderItem loaded = loader.Load (items, true);
+ if (loaded != ImageLoaderItem.None)
+ handler (loader, new ItemsCompletedEventArgs (loaded));
+ }
+
+ // Sync loading
+ public static void Load (this IImageLoader loader, ImageLoaderItem items)
+ {
+ loader.Load (items, false);
+ }
+
+ // Accessors
+ public static Pixbuf Pixbuf (this IImageLoader loader, ImageLoaderItem item)
+ {
+ if (item == ImageLoaderItem.Thumbnail)
+ return loader.Thumbnail;
+ else if (item == ImageLoaderItem.Large)
+ return loader.Large;
+ else if (item == ImageLoaderItem.Full)
+ return loader.Full;
+ throw new Exception ("Unknown item requested: "+item);
+ }
+
+ public static PixbufOrientation PixbufOrientation (this IImageLoader loader, ImageLoaderItem item)
+ {
+ if (item == ImageLoaderItem.Thumbnail)
+ return loader.ThumbnailOrientation;
+ else if (item == ImageLoaderItem.Large)
+ return loader.LargeOrientation;
+ else if (item == ImageLoaderItem.Full)
+ return loader.FullOrientation;
+ throw new Exception ("Unknown item requested: "+item);
+ }
+ }
+}
diff --git a/src/Loaders/ImageLoader.cs b/src/Loaders/ImageLoader.cs
index 7f81115..0e1f135 100644
--- a/src/Loaders/ImageLoader.cs
+++ b/src/Loaders/ImageLoader.cs
@@ -84,7 +84,7 @@ namespace FSpot.Loaders {
throw new Exception ("Loader requested for unknown file type: "+extension);
}
- loader = (IImageLoader) System.Activator.CreateInstance (t);
+ loader = (IImageLoader) System.Activator.CreateInstance (t, new object[] { uri });
return loader;
}
diff --git a/src/Loaders/ImageLoaderItem.cs b/src/Loaders/ImageLoaderItem.cs
new file mode 100644
index 0000000..268c0f2
--- /dev/null
+++ b/src/Loaders/ImageLoaderItem.cs
@@ -0,0 +1,21 @@
+//
+// Fspot/Loaders/ImageLoaderItem.cs
+//
+// Author(s)
+// Ruben Vermeersch <ruben savanne be>
+//
+// This is free software. See COPYING for details
+//
+
+using System;
+
+namespace FSpot.Loaders {
+ // Different bits of data which can be extracted
+ [Flags]
+ public enum ImageLoaderItem {
+ None = 0,
+ Thumbnail = 1 << 0, // A small thumbnail
+ Large = 1 << 1, // A large image for displaying (should load reasonably fast)
+ Full = 1 << 2 // The full image, for processing purposes (potentially very slow)
+ }
+}
diff --git a/src/Loaders/ImageLoaderItemExtensions.cs b/src/Loaders/ImageLoaderItemExtensions.cs
new file mode 100644
index 0000000..860684c
--- /dev/null
+++ b/src/Loaders/ImageLoaderItemExtensions.cs
@@ -0,0 +1,30 @@
+//
+// Fspot/Loaders/ImageLoaderItemExtensions.cs
+//
+// Author(s)
+// Ruben Vermeersch <ruben savanne be>
+//
+// This is free software. See COPYING for details
+//
+
+using System;
+
+namespace FSpot.Loaders {
+ public static class ImageLoaderItemExtensions {
+ public static bool Contains (this ImageLoaderItem item, ImageLoaderItem target)
+ {
+ return (item & target) == target;
+ }
+
+ public static ImageLoaderItem Largest (this ImageLoaderItem items)
+ {
+ if (items.Contains (ImageLoaderItem.Full))
+ return ImageLoaderItem.Full;
+ if (items.Contains (ImageLoaderItem.Large))
+ return ImageLoaderItem.Large;
+ if (items.Contains (ImageLoaderItem.Thumbnail))
+ return ImageLoaderItem.Thumbnail;
+ return ImageLoaderItem.None;
+ }
+ }
+}
diff --git a/src/Loaders/ItemCompletedEventArgs.cs b/src/Loaders/ItemCompletedEventArgs.cs
new file mode 100644
index 0000000..86be849
--- /dev/null
+++ b/src/Loaders/ItemCompletedEventArgs.cs
@@ -0,0 +1,22 @@
+//
+// Fspot/Loaders/ItemsCompletedEventArgs.cs
+//
+// Author(s)
+// Ruben Vermeersch <ruben savanne be>
+//
+// This is free software. See COPYING for details
+//
+
+using System;
+
+namespace FSpot.Loaders {
+ public class ItemsCompletedEventArgs : EventArgs
+ {
+ public ImageLoaderItem Items { get; private set; }
+
+ public ItemsCompletedEventArgs (ImageLoaderItem items) : base ()
+ {
+ this.Items = items;
+ }
+ }
+}
diff --git a/src/Loaders/LibrawImageLoader.cs b/src/Loaders/LibrawImageLoader.cs
index 06d6d25..5426864 100644
--- a/src/Loaders/LibrawImageLoader.cs
+++ b/src/Loaders/LibrawImageLoader.cs
@@ -10,122 +10,280 @@
//
using FSpot.Loaders.Native;
+using FSpot.Platform;
using FSpot.Utils;
using Gdk;
using System;
using System.Threading;
namespace FSpot.Loaders {
- public class LibrawImageLoader : IImageLoader {
- NativeLibrawLoader loader;
+ public class LibrawImageLoader : IImageLoader
+ {
Uri uri;
+ object sync_handle = new object ();
bool is_disposed = false;
- bool is_loading = false;
+ Rectangle damage;
+
+ public ImageLoaderItem ItemsRequested { get; private set; }
+ public ImageLoaderItem ItemsCompleted { get; private set; }
+
+ Pixbuf thumbnail;
+ public Pixbuf Thumbnail {
+ get { return PixbufUtils.ShallowCopy (thumbnail); }
+ private set { thumbnail = value; }
+ }
+ public PixbufOrientation ThumbnailOrientation { get; private set; }
+
+ Pixbuf large;
+ public Pixbuf Large {
+ get { return PixbufUtils.ShallowCopy (large); }
+ }
+ public PixbufOrientation LargeOrientation { get; private set; }
+
+ Pixbuf full;
+ public Pixbuf Full {
+ get { return PixbufUtils.ShallowCopy (full); }
+ }
+ public PixbufOrientation FullOrientation { get; private set; }
+
+ public event EventHandler<AreaPreparedEventArgs> AreaPrepared;
+ public event EventHandler<AreaUpdatedEventArgs> AreaUpdated;
+ public event EventHandler<ItemsCompletedEventArgs> Completed;
- Pixbuf thumb, full;
+ public bool Loading { get; private set; }
- public void Load (Uri uri)
+ NativeLibrawLoader loader;
+
+#region public api
+ public LibrawImageLoader (Uri uri) : base ()
{
- if (this.uri != null)
- throw new Exception ("You should only request one image per loader!");
this.uri = uri;
+ Loading = false;
- if (is_disposed)
- return;
+ ItemsRequested = ImageLoaderItem.None;
+ ItemsCompleted = ImageLoaderItem.None;
loader = new NativeLibrawLoader (uri.AbsolutePath);
- LoadThumbnail ();
- ThreadPool.QueueUserWorkItem (delegate { LoadFull (); });
+ }
+
+ public ImageLoaderItem Load (ImageLoaderItem items, bool async)
+ {
+ if (is_disposed)
+ return ImageLoaderItem.None;
+
+ ItemsRequested |= items;
+
+ StartLoading ();
+
+ if (!async)
+ WaitForCompletion (items);
+
+ return ItemsCompleted & items;
+ }
+
+ public void Dispose ()
+ {
+ is_disposed = true;
+ if (loader != null) {
+ loader.Aborted = true;
+ loader = null;
+ }
+ if (thumbnail != null)
+ thumbnail.Dispose ();
+ if (large != null)
+ large.Dispose ();
+ if (full != null)
+ full.Dispose ();
+ }
+#endregion
+
+#region private stuffs
+ void StartLoading ()
+ {
+ lock (sync_handle) {
+ if (Loading)
+ return;
+ Loading = true;
+ }
+
+ // Load thumbnail immediately, if required
+ if (!ItemsCompleted.Contains (ImageLoaderItem.Thumbnail) &&
+ ItemsRequested.Contains (ImageLoaderItem.Thumbnail)) {
+ LoadThumbnail ();
+ }
+
+ ThreadPool.QueueUserWorkItem (delegate {
+ try {
+ DoLoad ();
+ } catch (Exception e) {
+ Log.Debug (e.ToString ());
+ Log.Debug ("Requested: {0}, Done: {1}", ItemsRequested, ItemsCompleted);
+ Gtk.Application.Invoke (delegate { throw e; });
+ }
+ });
+ }
+
+ void DoLoad ()
+ {
+ while (!is_disposed && !ItemsCompleted.Contains (ItemsRequested)) {
+ if (ItemsRequested.Contains (ImageLoaderItem.Thumbnail))
+ LoadThumbnail ();
+
+ if (ItemsRequested.Contains (ImageLoaderItem.Large))
+ LoadLarge ();
+
+ if (ItemsRequested.Contains (ImageLoaderItem.Full))
+ LoadFull ();
+ }
+
+ lock (sync_handle) {
+ Loading = false;
+ }
}
void LoadThumbnail ()
{
+ if (is_disposed)
+ return;
+
+ // Check if the thumbnail exists, if not: try to create it from the
+ // Large image. Will request Large if it is not present and wait
+ // for the next call to generate it (see the loop in DoLoad).
+ if (!ThumbnailFactory.ThumbnailExists (uri)) {
+ if (ItemsCompleted.Contains (ImageLoaderItem.Large)) {
+ ThumbnailFactory.SaveThumbnail (Large, uri);
+ } else {
+ ItemsRequested |= ImageLoaderItem.Large;
+ return;
+ }
+ }
+
+ Thumbnail = ThumbnailFactory.LoadThumbnail (uri);
+ ThumbnailOrientation = PixbufOrientation.TopLeft;
+ if (Thumbnail == null)
+ throw new Exception ("Null thumbnail returned");
+
+ SignalAreaPrepared (ImageLoaderItem.Thumbnail);
+ SignalAreaUpdated (ImageLoaderItem.Thumbnail, new Rectangle (0, 0, Thumbnail.Width, Thumbnail.Height));
+ SignalItemCompleted (ImageLoaderItem.Thumbnail);
+ }
+
+
+ void LoadLarge ()
+ {
+ if (is_disposed)
+ return;
+
int orientation;
- thumb = loader.LoadThumbnail (out orientation);
+ large = loader.LoadEmbedded (out orientation);
switch (orientation) {
case 0:
- PixbufOrientation = PixbufOrientation.TopLeft;
+ LargeOrientation = PixbufOrientation.TopLeft;
break;
case 3:
- PixbufOrientation = PixbufOrientation.BottomRight;
+ LargeOrientation = PixbufOrientation.BottomRight;
break;
case 5:
- PixbufOrientation = PixbufOrientation.LeftBottom;
+ LargeOrientation = PixbufOrientation.LeftBottom;
break;
case 6:
- PixbufOrientation = PixbufOrientation.RightBottom;
+ LargeOrientation = PixbufOrientation.RightBottom;
break;
default:
throw new Exception ("Unexpected orientation returned!");
}
- GLib.Idle.Add (delegate {
- EventHandler<AreaPreparedEventArgs> prep = AreaPrepared;
- if (prep != null)
- prep (this, new AreaPreparedEventArgs (true));
- EventHandler<AreaUpdatedEventArgs> upd = AreaUpdated;
- if (upd != null)
- upd (this, new AreaUpdatedEventArgs (new Rectangle (0, 0, thumb.Width, thumb.Height)));
- return false;
- });
+ SignalAreaPrepared (ImageLoaderItem.Large);
+ SignalAreaUpdated (ImageLoaderItem.Large, new Rectangle (0, 0, large.Width, large.Height));
+ SignalItemCompleted (ImageLoaderItem.Large);
}
void LoadFull ()
{
+ if (is_disposed)
+ return;
+
loader.ProgressUpdated += delegate (object o, ProgressUpdatedArgs args) {
Log.Debug ("Loading RAW: {0}/{1}", args.Done, args.Total);
};
full = loader.LoadFull ();
+ FullOrientation = PixbufOrientation.TopLeft;
if (full == null) {
return;
}
- PixbufOrientation = PixbufOrientation.TopLeft;
- GLib.Idle.Add (delegate {
- EventHandler<AreaPreparedEventArgs> prep = AreaPrepared;
- if (prep != null)
- prep (this, new AreaPreparedEventArgs (false));
- EventHandler<AreaUpdatedEventArgs> upd = AreaUpdated;
- if (upd != null)
- upd (this, new AreaUpdatedEventArgs (new Rectangle (0, 0, full.Width, full.Height)));
- EventHandler eh = Completed;
- if (eh != null)
- eh (this, EventArgs.Empty);
- return false;
- });
- }
-
- public event EventHandler<AreaPreparedEventArgs> AreaPrepared;
- public event EventHandler<AreaUpdatedEventArgs> AreaUpdated;
- public event EventHandler Completed;
-
- public bool Loading {
- get { return is_loading; }
+ SignalAreaPrepared (ImageLoaderItem.Full);
+ SignalAreaUpdated (ImageLoaderItem.Full, new Rectangle (0, 0, full.Width, full.Height));
+ SignalItemCompleted (ImageLoaderItem.Full);
}
- public void Dispose ()
+ void WaitForCompletion (ImageLoaderItem items)
{
- if (loader != null) {
- loader.Aborted = true;
- loader = null;
+ while (!ItemsCompleted.Contains(items)) {
+ Log.Debug ("Waiting for completion of {0} (done: {1})", ItemsRequested, ItemsCompleted);
+ Monitor.Enter (sync_handle);
+ Monitor.Wait (sync_handle);
+ Monitor.Exit (sync_handle);
+ Log.Debug ("Woke up after waiting for {0} (done: {1})", ItemsRequested, ItemsCompleted);
}
- if (thumb != null)
- thumb.Dispose ();
- if (full != null)
- full.Dispose ();
}
- public Pixbuf Pixbuf {
- get {
- return full == null ? PixbufUtils.ShallowCopy (thumb) : PixbufUtils.ShallowCopy (full);
+ void SignalAreaPrepared (ImageLoaderItem item) {
+ damage = Rectangle.Zero;
+ EventHandler<AreaPreparedEventArgs> eh = AreaPrepared;
+ if (eh != null)
+ GLib.Idle.Add (delegate {
+ eh (this, new AreaPreparedEventArgs (item));
+ return false;
+ });
+ }
+
+ void SignalAreaUpdated (ImageLoaderItem item, Rectangle area) {
+ EventHandler<AreaUpdatedEventArgs> eh = AreaUpdated;
+ if (eh == null)
+ return;
+
+ lock (sync_handle) {
+ if (damage == Rectangle.Zero) {
+ damage = area;
+ GLib.Idle.Add (delegate {
+ Rectangle to_signal;
+ lock (sync_handle) {
+ to_signal = damage;
+ damage = Rectangle.Zero;
+ }
+ eh (this, new AreaUpdatedEventArgs (item, to_signal));
+ return false;
+ });
+ } else {
+ damage = damage.Union (area);
+ }
}
}
- public PixbufOrientation PixbufOrientation { get; private set; }
+ void SignalItemCompleted (ImageLoaderItem item)
+ {
+ ItemsCompleted |= item;
+ Log.Debug ("Notifying completion of {0} (done: {1}, requested: {2})", item, ItemsCompleted, ItemsRequested);
+
+ Monitor.Enter (sync_handle);
+ Monitor.PulseAll (sync_handle);
+ Monitor.Exit (sync_handle);
+
+ Log.Debug ("Signalled!");
+
+ EventHandler<ItemsCompletedEventArgs> eh = Completed;
+ if (eh != null)
+ GLib.Idle.Add (delegate {
+ eh (this, new ItemsCompletedEventArgs (item));
+ return false;
+ });
+ }
+#endregion
}
}
diff --git a/src/Makefile.am b/src/Makefile.am
index f0f26fc..42f3c29 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -189,7 +189,11 @@ F_SPOT_CSDISTFILES = \
$(srcdir)/Loaders/AreaPreparedEventArgs.cs \
$(srcdir)/Loaders/AreaUpdatedEventArgs.cs \
$(srcdir)/Loaders/ImageLoader.cs \
+ $(srcdir)/Loaders/ImageLoaderItem.cs \
+ $(srcdir)/Loaders/ImageLoaderItemExtensions.cs \
$(srcdir)/Loaders/IImageLoader.cs \
+ $(srcdir)/Loaders/IImageLoaderExtensions.cs \
+ $(srcdir)/Loaders/ItemCompletedEventArgs.cs \
$(srcdir)/Loaders/GdkImageLoader.cs \
$(srcdir)/Loaders/LibrawImageLoader.cs \
$(srcdir)/ImageLoaderThread.cs \
diff --git a/src/PhotoImageView.cs b/src/PhotoImageView.cs
index d916ec1..450e252 100644
--- a/src/PhotoImageView.cs
+++ b/src/PhotoImageView.cs
@@ -166,17 +166,22 @@ namespace FSpot.Widgets {
#region loader
uint timer;
IImageLoader loader;
+ bool prepared_is_new;
+ ImageLoaderItem current_item;
+
void Load (Uri uri)
{
timer = Log.DebugTimerStart ();
if (loader != null)
loader.Dispose ();
+ current_item = ImageLoaderItem.None;
+
+ prepared_is_new = true;
loader = ImageLoader.Create (uri);
loader.AreaPrepared += HandlePixbufPrepared;
loader.AreaUpdated += HandlePixbufAreaUpdated;
- loader.Completed += HandleDone;
- loader.Load (uri);
+ loader.Load (ImageLoaderItem.Thumbnail | ImageLoaderItem.Large | ImageLoaderItem.Full, HandleCompleted);
}
void HandlePixbufPrepared (object sender, AreaPreparedEventArgs args)
@@ -188,9 +193,16 @@ namespace FSpot.Widgets {
if (!ShowProgress)
return;
- Gdk.Pixbuf prev = this.Pixbuf;
- this.Pixbuf = loader.Pixbuf;
- PixbufOrientation = Accelerometer.GetViewOrientation (loader.PixbufOrientation);
+ if (args.Item < current_item)
+ return;
+
+ current_item = args.Item.Largest ();
+
+ Gdk.Pixbuf prev = Pixbuf;
+ PixbufOrientation orientation = Accelerometer.GetViewOrientation (loader.PixbufOrientation (current_item));
+ ChangeImage (loader.Pixbuf (current_item), orientation, prepared_is_new, current_item != ImageLoaderItem.Full);
+ prepared_is_new = false;
+
if (prev != null)
prev.Dispose ();
@@ -210,7 +222,7 @@ namespace FSpot.Widgets {
this.QueueDrawArea (area.X, area.Y, area.Width, area.Height);
}
- void HandleDone (object sender, System.EventArgs args)
+ void HandleCompleted (object sender, ItemsCompletedEventArgs args)
{
Log.DebugTimerPrint (timer, "Loading image took {0}");
IImageLoader loader = sender as IImageLoader;
@@ -218,8 +230,10 @@ namespace FSpot.Widgets {
return;
Pixbuf prev = this.Pixbuf;
- if (Pixbuf != loader.Pixbuf)
- Pixbuf = loader.Pixbuf;
+ if (current_item != args.Items.Largest ()) {
+ current_item = args.Items.Largest ();
+ ChangeImage (loader.Pixbuf (current_item), Accelerometer.GetViewOrientation (loader.PixbufOrientation (current_item)), false, false);
+ }
if (Pixbuf == null) {
// FIXME: Do we have test cases for this ???
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]