Logo Search packages:      
Sourcecode: f-spot version File versions

PixbufCache.cs

using System.Collections;
using System.Threading;

namespace FSpot {
      public class PixbufCache {
            Hashtable items;
            ArrayList items_mru;
            int total_size;
            int max_size = 256 * 256 * 4 * 30;

            private Thread worker;

            public delegate void PixbufLoadedHandler (PixbufCache cache, CacheEntry entry);
            public event PixbufLoadedHandler OnPixbufLoaded;
            
            public PixbufCache ()
            {
                  items = new Hashtable ();
                  items_mru = new ArrayList ();
                  
                  worker = new Thread (new ThreadStart (WorkerTask));
                  worker.Start ();

                  ThumbnailGenerator.Default.OnPixbufLoaded += HandleThumbnailLoaded;
            }
            
            public void HandleThumbnailLoaded (PixbufLoader loader, string path, int order, Gdk.Pixbuf result)
            {
                  string thumb_path = ThumbnailGenerator.ThumbnailPath (new System.Uri (path));
                  
                  if (result != null)
                        Reload (thumb_path);
            }
                  
            public void Request (string path, object closure, int width, int height)
            {
                  lock (items) {
                        CacheEntry entry = items[path] as CacheEntry;

                        if (entry == null) {
                              entry = new CacheEntry (this, path, closure, width, height);
                              items [path] = entry;
                              items_mru.Add (entry);
                        } else {
                              MoveForward (entry);
                              entry.Data = closure;
                        }
                        Monitor.Pulse (items);
                  }
            }

            public void Update (string path, Gdk.Pixbuf pixbuf)
            {
                  lock (items) {
                        CacheEntry entry = (CacheEntry) items [path];
                        if (entry != null) {
                              entry.SetPixbufExtended (pixbuf, true);
                        }
                  }
            }

            public void Update (CacheEntry entry, Gdk.Pixbuf pixbuf)
            {
                  lock (items) {
                        entry.SetPixbufExtended (pixbuf, true);
                  }
            }
            
            public void Reload (CacheEntry entry, object data, int width, int height)
            {
                  lock (items) {
                        lock (entry) {
                              entry.Reload = true;
                              entry.Width = width;
                              entry.Height = height;
                              entry.Data = data;
                        }
                        Monitor.Pulse (items);
                  }
            }

            public void Reload (string path)
            {
                  CacheEntry entry;

                  lock (items) {
                        entry = (CacheEntry) items [path];
                        if (entry != null) {
                              lock (entry) {
                                    entry.Reload = true;
                              }
                              Monitor.Pulse (items);
                        }
                  }
            }

            private CacheEntry FindNext ()
            {
                  CacheEntry entry;
                  int i = items_mru.Count;
                  int size = 0;
                  if (total_size > max_size * 4) {
                        //System.Console.WriteLine ("Hit major limit ({0}) out of {1}",
                        //                  total_size, max_size);
                        return null;
                  } 
                  while (i-- > 0) {
                        entry = (CacheEntry) items_mru [i];
                        lock (entry) {
                              if (entry.Reload) {
                                    entry.Reload = false;
                                    return entry;
                              }

                              //if the depth of the queue is so large that we've reached double our limit 
                              //break out of here and let the queue shrink.
                              if (entry.Pixbuf != null)
                                    size += entry.Size;

                              if (size > max_size * 2) {
                                    //System.Console.WriteLine ("Hit limit ({0},{1}) out of {2}", 
                                    //                  size, total_size,max_size);
                                    return null;
                              }
                        }
                  }
                  return null;
            }
            
            private bool ShrinkIfNeeded ()
            {
                  int num = 0;
                  while ((items_mru.Count - num) > 10 && total_size > max_size) {
                        CacheEntry entry = (CacheEntry) items_mru [num++];
                        items.Remove (entry.Path);
                        entry.Dispose ();
                  }
                  if (num > 0) {
                        //System.Console.WriteLine ("removing {0} out of {3}  ({1} > {2})", 
                        //                  num, total_size, max_size, items_mru.Count);
                        items_mru.RemoveRange (0, num);
                        return true;
                  }
                  return false;
            }
            
            private void WorkerTask ()
            {
                  CacheEntry current = null;
                  //ThumbnailGenerator.Default.PushBlock ();
                  while (true) {
                        try {
                              lock (items) {
                                    /* find the next item */
                                    while ((current = FindNext ()) == null) {
                                          if (!ShrinkIfNeeded ()){
                                                //ThumbnailGenerator.Default.PopBlock ();
                                                Monitor.Wait (items);
                                                //ThumbnailGenerator.Default.PushBlock ();
                                          }
                                    }
                              }
                              ProcessRequest (current);
                              QueueLast (current);
                        } catch (System.Exception e) {
                              System.Console.WriteLine (e);
                              current = null;
                        }
                  }
            }
            
            protected virtual void ProcessRequest (CacheEntry entry)
            {
                  Gdk.Pixbuf loaded = null;
                  try {
                        loaded = new Gdk.Pixbuf (entry.Path);
                        this.Update (entry, loaded);
                  } catch (GLib.GException){
                        if (loaded != null)
                              loaded.Dispose ();
                        return;           
                  }
            }
            
            private void QueueLast (CacheEntry entry)
            {
                  Gtk.Application.Invoke (delegate (object obj, System.EventArgs args) {
                        if (entry.Path != null && OnPixbufLoaded != null)
                              OnPixbufLoaded (this, entry);
                  });
            }
            
            private void MoveForward (CacheEntry entry)
            {
#if true
                  int i = items_mru.Count;
                  CacheEntry tmp1 = entry;
                  CacheEntry tmp2;
                  while (i-- > 0) {
                        tmp2 = (CacheEntry) items_mru [i];
                        items_mru [i] = tmp1;
                        tmp1 = tmp2;
                        if (tmp2 == entry)
                              return;
                  }
                  throw new System.Exception ("move forward failed");
#else
                  items_mru.Remove (entry);
                  items_mru.Add (entry);  
#endif
            }
                   

            private CacheEntry ULookup (string path)
            {
                  CacheEntry entry = (CacheEntry) items [path];
                  if (entry != null) {
                        MoveForward (entry);
                  }
                  return (CacheEntry) entry;
            }

            public CacheEntry Lookup (string path)
            {
                  lock (items) {
                        return ULookup (path);
                  }
            }

            public void Remove (string path) 
            {
                  lock (items) {
                        URemove (path);
                  }
            }

            private void URemove (string path)
            {
                  CacheEntry entry = (CacheEntry) items [path];
                  if (entry != null) {
                        items.Remove (path);
                        items_mru.Remove (entry);
                        entry.Dispose ();
                  }
            }

            public class CacheEntry : System.IDisposable {
                  private Gdk.Pixbuf pixbuf;
                  private string path;
                  private int width;
                  private int height;
                  private object data;
                  private bool reload;
                  private PixbufCache cache;
                  
                  public CacheEntry (PixbufCache cache, string path, object closure, int width, int height)
                  {
                        this.path = path;
                        this.width = width;
                        this.height = height;
                        this.data = closure;
                        this.Reload = true;
                        this.cache = cache;
                        cache.total_size += this.Size;
                  }

                  public bool Reload {
                        get {
                              return reload;
                        }
                        set {
                              reload = value;
                        }
                  }

                  public string Path {
                        get {
                              return path;
                        }
                  }

                  public int Width {
                        get {
                              return width;
                        }
                        set {
                              width = value;
                        }
                  }

                  public int Height {
                        get {
                              return height;
                        }
                        set {
                              height = value;
                        }
                  }

                  public object Data {
                        get {
                              lock (this) {
                                    return data;
                              }
                        }
                        set {
                              lock (this) {
                                    data = value;
                              }
                        }
                  }
                  
                  public bool IsDisposed {
                        get { return path == null; }
                  }
                  
                  public void SetPixbufExtended (Gdk.Pixbuf value, bool ignore_undead)
                  {
                        lock (this) {
                              if (IsDisposed) {
                                    if (ignore_undead) {
                                          return;
                                    } else {
                                          throw new System.Exception ("I don't want to be undead");
                                    }
                              }                                         

                              Gdk.Pixbuf old = this.Pixbuf;
                              cache.total_size -= this.Size;
                              this.pixbuf = value;
                              if (pixbuf != null) {
                                    this.width = pixbuf.Width;
                                    this.height = pixbuf.Height;
                              }
                              cache.total_size += this.Size;
                              this.Reload = false;
                              
                              if (old != null)
                                    old.Dispose ();
                        }
                  }

                  public Gdk.Pixbuf Pixbuf {
                        get {
                              lock (this) {
                                    return pixbuf;
                              }
                        }
                  }
                  
                  public Gdk.Pixbuf ShallowCopyPixbuf ()
                  {
                        lock (this) {
                              if (IsDisposed)
                                    throw new System.Exception ("I'm dead");

                              if (pixbuf == null)
                                    return null;
                              
                              return PixbufUtils.ShallowCopy (pixbuf);
                        }
                  }
                  
                  ~CacheEntry ()
                  {
                        if (!IsDisposed)
                              this.Dispose ();
                  }
                  
                  public void Dispose ()
                  {
                        lock (this) {
                              if (! IsDisposed)
                                    cache.total_size -= this.Size;

                              if (this.pixbuf != null) {
                                    this.pixbuf.Dispose ();
                                    
                              }
                              this.pixbuf = null;
                              this.cache = null;
                              this.path = null;
                        }
                        System.GC.SuppressFinalize (this);
                  }
                  
                  public int Size {
                        get {
                              return width * height * 3;
                        }
                  }
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index