banshee r4660 - in trunk/banshee: . src/Clients/Muinshee/Muinshee src/Core/Banshee.ThickClient/Banshee.Collection.Gui src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor src/Core/Banshee.ThickClient/Banshee.Gui.Widgets src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage src/Dap/Banshee.Dap.Mtp/Banshee.Dap.Mtp src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui src/Libraries/Hyena src/Libraries/Hyena.Gui/Hyena.Gui src/Libraries/Hyena/Hyena.Collections
- From: abock svn gnome org
- To: svn-commits-list gnome org
- Subject: banshee r4660 - in trunk/banshee: . src/Clients/Muinshee/Muinshee src/Core/Banshee.ThickClient/Banshee.Collection.Gui src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor src/Core/Banshee.ThickClient/Banshee.Gui.Widgets src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage src/Dap/Banshee.Dap.Mtp/Banshee.Dap.Mtp src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui src/Libraries/Hyena src/Libraries/Hyena.Gui/Hyena.Gui src/Libraries/Hyena/Hyena.Collections
- Date: Mon, 6 Oct 2008 21:04:57 +0000 (UTC)
Author: abock
Date: Mon Oct 6 21:04:57 2008
New Revision: 4660
URL: http://svn.gnome.org/viewvc/banshee?rev=4660&view=rev
Log:
2008-10-06 Aaron Bockover <abock gnome org>
This commit finishes the port to using Cairo surface in almost all places
where we do rendering, and adds a surface cache system for improving
performance in some places.
* src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellAlbum.cs:
* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcast.cs:
Use the new surface methods from artwork manager to directly render
surfaces as stored in the LRU cache
* src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ArtworkManager.cs:
Implement a surface caching system that should help improve the rendering
speed of the album browser. Tuned to never grow beyond 1MB of image data
per cache.
* src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ArtworkRenderer.cs:
Accept surfaces as input instead of pixbufs. Yay.
* src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TrackInfoDisplay.cs:
Port the beast to use cairo image surfaces directly intead of pixbufs,
get rid of the surface cache hack
* src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/LargeTrackInfoDisplay.cs:
Updated to reflect the surface changes in base class, cache the scene
surfaces to avoid re-rendering on each pass (was cached before, but using
a different mechanism)
* src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ClassicTrackInfoDisplay.cs:
* src/Clients/Muinshee/Muinshee/MuinsheeTrackInfoDisplay.cs:
Updated to reflect surface changes in the base class
* src/Libraries/Hyena/Hyena.Collections/LruCache.cs: Implement a somewhat
lame generic LRU cache collection
* src/Libraries/Hyena.Gui/Hyena.Gui/PixbufImageSurface.cs: Add a new
ctor that disposes the input pixbuf when done with it, nice for
inlining pixbuf->surface without temporary variables or leaking
* src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs:
* src/Dap/Banshee.Dap.Mtp/Banshee.Dap.Mtp/MtpSource.cs:
* src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs:
* src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs:
* src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs:
Updated to reflect API rename in the artwork manager
Added:
trunk/banshee/src/Libraries/Hyena/Hyena.Collections/LruCache.cs
Modified:
trunk/banshee/ChangeLog
trunk/banshee/src/Clients/Muinshee/Muinshee/MuinsheeTrackInfoDisplay.cs
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ArtworkManager.cs
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ArtworkRenderer.cs
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellAlbum.cs
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ClassicTrackInfoDisplay.cs
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/LargeTrackInfoDisplay.cs
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TrackInfoDisplay.cs
trunk/banshee/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs
trunk/banshee/src/Dap/Banshee.Dap.Mtp/Banshee.Dap.Mtp/MtpSource.cs
trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs
trunk/banshee/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs
trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcast.cs
trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui/PixbufImageSurface.cs
trunk/banshee/src/Libraries/Hyena/Hyena.csproj
trunk/banshee/src/Libraries/Hyena/Makefile.am
Modified: trunk/banshee/src/Clients/Muinshee/Muinshee/MuinsheeTrackInfoDisplay.cs
==============================================================================
--- trunk/banshee/src/Clients/Muinshee/Muinshee/MuinsheeTrackInfoDisplay.cs (original)
+++ trunk/banshee/src/Clients/Muinshee/Muinshee/MuinsheeTrackInfoDisplay.cs Mon Oct 6 21:04:57 2008
@@ -28,13 +28,15 @@
using System;
+using Cairo;
+using Hyena.Gui;
using Banshee.Collection.Gui;
namespace Muinshee
{
public class MuinsheeTrackInfoDisplay : Banshee.Gui.Widgets.ClassicTrackInfoDisplay
{
- private Gdk.Pixbuf idle_album;
+ private ImageSurface idle_album;
public MuinsheeTrackInfoDisplay () : base ()
{
@@ -46,7 +48,9 @@
protected override void RenderIdle (Cairo.Context cr)
{
- idle_album = idle_album ?? Banshee.Gui.IconThemeUtils.LoadIcon (ArtworkSizeRequest, "media-optical");
+ idle_album = idle_album ?? new PixbufImageSurface (Banshee.Gui.IconThemeUtils.LoadIcon (
+ ArtworkSizeRequest, "media-optical"), true);
+
ArtworkRenderer.RenderThumbnail (cr, idle_album, false, Allocation.X, Allocation.Y,
ArtworkSizeRequest, ArtworkSizeRequest,
false, 0, true, BackgroundColor);
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ArtworkManager.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ArtworkManager.cs (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ArtworkManager.cs Mon Oct 6 21:04:57 2008
@@ -36,6 +36,9 @@
using Gdk;
using Hyena;
+using Hyena.Gui;
+using Hyena.Collections;
+
using Banshee.Base;
using Banshee.ServiceStack;
@@ -43,6 +46,22 @@
{
public class ArtworkManager : IService
{
+ private Dictionary<int, SurfaceCache> scale_caches = new Dictionary<int, SurfaceCache> ();
+
+ private class SurfaceCache : LruCache<string, Cairo.ImageSurface>
+ {
+ public SurfaceCache (int max_items) : base (max_items)
+ {
+ }
+
+ protected override void ExpireItem (Cairo.ImageSurface item)
+ {
+ if (item != null) {
+ item.Destroy ();
+ }
+ }
+ }
+
public ArtworkManager ()
{
try {
@@ -77,31 +96,68 @@
Log.Debug (String.Format ("Migrated {0} album art images.", artwork_count));
}
- public Cairo.Surface Lookup (Cairo.Context cr, string id)
+ public Cairo.ImageSurface LookupSurface (string id)
{
- return LookupScale (cr, id, 0);
+ return LookupScaleSurface (id, 0);
}
- public Cairo.Surface LookupScale (Cairo.Context cr, string id, int size)
+ public Cairo.ImageSurface LookupScaleSurface (string id, int size)
{
- Pixbuf pixbuf = LookupScale (id, size);
+ return LookupScaleSurface (id, size, false);
+ }
+
+ public Cairo.ImageSurface LookupScaleSurface (string id, int size, bool useCache)
+ {
+ SurfaceCache cache = null;
+ Cairo.ImageSurface surface = null;
+
+ if (id == null) {
+ return null;
+ }
+
+ if (useCache && scale_caches.TryGetValue (size, out cache) && cache.TryGetValue (id, out surface)) {
+ return surface;
+ }
+
+ Pixbuf pixbuf = LookupScalePixbuf (id, size);
if (pixbuf == null) {
return null;
}
try {
- return Hyena.Gui.CairoExtensions.CreateSurfaceForPixbuf (cr, pixbuf);
+ surface = new PixbufImageSurface (pixbuf);
+ if (surface == null) {
+ return null;
+ }
+
+ if (!useCache) {
+ return surface;
+ }
+
+ if (cache == null) {
+ int bytes = 4 * size * size;
+ int max = (1 << 20) / bytes;
+
+ Log.DebugFormat ("Creating new surface cache for {0} KB (max) images, capped at 1 MB ({1} items)",
+ bytes, max);
+
+ cache = new SurfaceCache (max);
+ scale_caches.Add (size, cache);
+ }
+
+ cache.Add (id, surface);
+ return surface;
} finally {
- pixbuf.Dispose ();
+ DisposePixbuf (pixbuf);
}
}
- public Pixbuf Lookup (string id)
+ public Pixbuf LookupPixbuf (string id)
{
- return LookupScale (id, 0);
+ return LookupScalePixbuf (id, 0);
}
- public Pixbuf LookupScale (string id, int size)
+ public Pixbuf LookupScalePixbuf (string id, int size)
{
if (id == null || (size != 0 && size < 10)) {
return null;
@@ -147,7 +203,7 @@
Pixbuf scaled_pixbuf = pixbuf.ScaleSimple (size, size, Gdk.InterpType.Bilinear);
Directory.CreateDirectory (Path.GetDirectoryName (path));
scaled_pixbuf.Save (path, "jpeg");
- ArtworkRenderer.DisposePixbuf (pixbuf);
+ DisposePixbuf (pixbuf);
return scaled_pixbuf;
} catch {}
}
@@ -155,6 +211,23 @@
return null;
}
+ private static int dispose_count = 0;
+ public static void DisposePixbuf (Pixbuf pixbuf)
+ {
+ if (pixbuf != null && pixbuf.Handle != IntPtr.Zero) {
+ pixbuf.Dispose ();
+ pixbuf = null;
+
+ // There is an issue with disposing Pixbufs where we need to explicitly
+ // call the GC otherwise it doesn't get done in a timely way. But if we
+ // do it every time, it slows things down a lot; so only do it every 100th.
+ if (++dispose_count % 100 == 0) {
+ System.GC.Collect ();
+ dispose_count = 0;
+ }
+ }
+ }
+
string IService.ServiceName {
get { return "ArtworkManager"; }
}
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ArtworkRenderer.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ArtworkRenderer.cs (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ArtworkRenderer.cs Mon Oct 6 21:04:57 2008
@@ -38,33 +38,33 @@
private static Color cover_border_light_color = new Color (1.0, 1.0, 1.0, 0.5);
private static Color cover_border_dark_color = new Color (0.0, 0.0, 0.0, 0.65);
- public static void RenderThumbnail (Cairo.Context cr, Gdk.Pixbuf pixbuf, bool dispose,
+ public static void RenderThumbnail (Cairo.Context cr, ImageSurface image, bool dispose,
double x, double y, double width, double height, bool drawBorder, double radius)
{
- RenderThumbnail (cr, pixbuf, dispose, x, y, width, height,
+ RenderThumbnail (cr, image, dispose, x, y, width, height,
drawBorder, radius, false, cover_border_light_color);
}
- public static void RenderThumbnail (Cairo.Context cr, Gdk.Pixbuf pixbuf, bool dispose,
+ public static void RenderThumbnail (Cairo.Context cr, ImageSurface image, bool dispose,
double x, double y, double width, double height, bool drawBorder, double radius,
bool fill, Color fillColor)
{
- RenderThumbnail (cr, pixbuf, dispose, x, y, width, height, drawBorder, radius,
+ RenderThumbnail (cr, image, dispose, x, y, width, height, drawBorder, radius,
fill, fillColor, CairoCorners.All);
}
- public static void RenderThumbnail (Cairo.Context cr, Gdk.Pixbuf pixbuf, bool dispose,
+ public static void RenderThumbnail (Cairo.Context cr, ImageSurface image, bool dispose,
double x, double y, double width, double height, bool drawBorder, double radius,
bool fill, Color fillColor, CairoCorners corners)
{
- if (pixbuf == null || pixbuf.Handle == IntPtr.Zero) {
+ if (image == null || image.Handle == IntPtr.Zero) {
return;
}
double p_x = x;
double p_y = y;
- p_x += pixbuf.Width < width ? (width - pixbuf.Width) / 2 : 0;
- p_y += pixbuf.Height < height ? (height - pixbuf.Height) / 2 : 0;
+ p_x += image.Width < width ? (width - image.Width) / 2 : 0;
+ p_y += image.Height < height ? (height - image.Height) / 2 : 0;
cr.Antialias = Cairo.Antialias.Default;
@@ -74,13 +74,13 @@
cr.Fill();
}
- CairoExtensions.RoundedRectangle (cr, p_x, p_y, pixbuf.Width, pixbuf.Height, radius, corners);
- Gdk.CairoHelper.SetSourcePixbuf (cr, pixbuf, p_x, p_y);
+ CairoExtensions.RoundedRectangle (cr, p_x, p_y, image.Width, image.Height, radius, corners);
+ cr.SetSource (image, p_x, p_y);
cr.Fill ();
if (!drawBorder) {
if (dispose) {
- DisposePixbuf (pixbuf);
+ image.Destroy ();
}
return;
@@ -100,24 +100,7 @@
cr.Stroke ();
if (dispose) {
- DisposePixbuf (pixbuf);
- }
- }
-
- private static int dispose_count = 0;
- public static void DisposePixbuf (Gdk.Pixbuf pixbuf)
- {
- if (pixbuf != null && pixbuf.Handle != IntPtr.Zero) {
- pixbuf.Dispose ();
- pixbuf = null;
-
- // There is an issue with disposing Pixbufs where we need to explicitly
- // call the GC otherwise it doesn't get done in a timely way. But if we
- // do it every time, it slows things down a lot; so only do it every 100th.
- if (++dispose_count % 100 == 0) {
- GC.Collect ();
- dispose_count = 0;
- }
+ image.Destroy ();
}
}
}
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellAlbum.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellAlbum.cs (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellAlbum.cs Mon Oct 6 21:04:57 2008
@@ -41,10 +41,11 @@
{
public class ColumnCellAlbum : ColumnCell
{
- private static int pixbuf_spacing = 4;
- private static int pixbuf_size = 48;
+ private static int image_spacing = 4;
+ private static int image_size = 48;
- private static Gdk.Pixbuf default_cover_pixbuf = IconThemeUtils.LoadIcon (pixbuf_size, "media-optical");
+ private static ImageSurface default_cover_image
+ = new PixbufImageSurface (IconThemeUtils.LoadIcon (image_size, "media-optical"));
private ArtworkManager artwork_manager;
@@ -66,20 +67,21 @@
AlbumInfo album = (AlbumInfo)BoundObject;
bool is_default = false;
- Gdk.Pixbuf pixbuf = artwork_manager == null ? null : artwork_manager.LookupScale (album.ArtworkId, pixbuf_size);
+ ImageSurface image = artwork_manager == null ? null
+ : artwork_manager.LookupScaleSurface (album.ArtworkId, image_size, true);
- if (pixbuf == null) {
- pixbuf = default_cover_pixbuf;
+ if (image == null) {
+ image = default_cover_image;
is_default = true;
}
- // int pixbuf_render_size = is_default ? pixbuf.Height : (int)cellHeight - 8;
- int pixbuf_render_size = pixbuf_size;
- int x = pixbuf_spacing;
- int y = ((int)cellHeight - pixbuf_render_size) / 2;
+ // int image_render_size = is_default ? image.Height : (int)cellHeight - 8;
+ int image_render_size = image_size;
+ int x = image_spacing;
+ int y = ((int)cellHeight - image_render_size) / 2;
- ArtworkRenderer.RenderThumbnail (context.Context, pixbuf, !is_default, x, y,
- pixbuf_render_size, pixbuf_render_size, !is_default, context.Theme.Context.Radius);
+ ArtworkRenderer.RenderThumbnail (context.Context, image, false, x, y,
+ image_render_size, image_render_size, !is_default, context.Theme.Context.Radius);
int fl_width = 0, fl_height = 0, sl_width = 0, sl_height = 0;
Cairo.Color text_color = context.Theme.Colors.GetWidgetColor (GtkColorClass.Text, state);
@@ -151,7 +153,7 @@
layout.Dispose ();
- return (height < pixbuf_size ? pixbuf_size : height) + 6;
+ return (height < image_size ? image_size : height) + 6;
}
}
}
\ No newline at end of file
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs Mon Oct 6 21:04:57 2008
@@ -389,7 +389,7 @@
}
ArtworkManager artwork = ServiceManager.Get<ArtworkManager> ();
- Gdk.Pixbuf cover_art = artwork.LookupScale (current_track.ArtworkId, 64);
+ Gdk.Pixbuf cover_art = artwork.LookupScalePixbuf (current_track.ArtworkId, 64);
header_image.Pixbuf = cover_art;
if (cover_art == null) {
header_image.IconName = "media-optical";
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ClassicTrackInfoDisplay.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ClassicTrackInfoDisplay.cs (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ClassicTrackInfoDisplay.cs Mon Oct 6 21:04:57 2008
@@ -272,7 +272,7 @@
return false;
}
- Gdk.Pixbuf pixbuf = ArtworkManager.Lookup (CurrentTrack.ArtworkId);
+ Gdk.Pixbuf pixbuf = ArtworkManager.LookupPixbuf (CurrentTrack.ArtworkId);
if (pixbuf == null) {
HidePopup ();
@@ -299,7 +299,7 @@
private void HidePopup ()
{
if (popup != null) {
- ArtworkRenderer.DisposePixbuf (popup.Image);
+ ArtworkManager.DisposePixbuf (popup.Image);
popup.Destroy ();
popup.EnterNotifyEvent -= OnPopupEnterNotifyEvent;
popup.LeaveNotifyEvent -= OnPopupLeaveNotifyEvent;
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/LargeTrackInfoDisplay.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/LargeTrackInfoDisplay.cs (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/LargeTrackInfoDisplay.cs Mon Oct 6 21:04:57 2008
@@ -41,6 +41,7 @@
public class LargeTrackInfoDisplay : TrackInfoDisplay
{
private Gdk.Rectangle text_alloc;
+ private Dictionary<ImageSurface, Surface> surfaces = new Dictionary<ImageSurface, Surface> ();
public LargeTrackInfoDisplay ()
{
@@ -82,26 +83,26 @@
QueueDraw ();
}
- protected override void RenderCoverArt (Cairo.Context cr, Gdk.Pixbuf pixbuf)
+ protected override void RenderCoverArt (Cairo.Context cr, ImageSurface image)
{
- if (pixbuf == null) {
+ if (image == null) {
return;
}
Gdk.Rectangle alloc = RenderAllocation;
int asr = ArtworkSizeRequest;
- int reflect = (int)(pixbuf.Height * 0.2);
- int surface_w = pixbuf.Width;
- int surface_h = pixbuf.Height + reflect;
+ int reflect = (int)(image.Height * 0.2);
+ int surface_w = image.Width;
+ int surface_h = image.Height + reflect;
int x = alloc.X + alloc.Width - asr;
int y = alloc.Y;
- Surface surface = SurfaceLookup (pixbuf);
- if (surface == null) {
- surface = CreateSurfaceForPixbuf (cr, pixbuf, reflect);
- SurfaceCache (pixbuf, surface);
+ Surface scene = null;
+ if (!surfaces.TryGetValue (image, out scene)) {
+ scene = CreateScene (cr, image, reflect);
+ surfaces.Add (image, scene);
}
-
+
cr.Rectangle (x, y, asr, alloc.Height);
cr.Color = BackgroundColor;
cr.Fill ();
@@ -109,32 +110,30 @@
x += (asr - surface_w) / 2;
y += surface_h > asr ? 0 : (asr - surface_h) / 2;
- cr.SetSource (surface, x, y);
+ cr.SetSource (scene, x, y);
cr.Paint ();
}
- private Surface CreateSurfaceForPixbuf (Cairo.Context window_cr, Gdk.Pixbuf pixbuf, int reflect)
+ private Surface CreateScene (Cairo.Context window_cr, ImageSurface image, int reflect)
{
Surface surface = window_cr.Target.CreateSimilar (window_cr.Target.Content,
- pixbuf.Width, pixbuf.Height + reflect);
+ image.Width, image.Height + reflect);
Cairo.Context cr = new Context (surface);
cr.Save ();
- ImageSurface img = new PixbufImageSurface (pixbuf);
-
- cr.SetSource (img);
+ cr.SetSource (image);
cr.Paint ();
- cr.Rectangle (0, pixbuf.Height, pixbuf.Width, reflect);
+ cr.Rectangle (0, image.Height, image.Width, reflect);
cr.Clip ();
Matrix matrix = new Matrix ();
matrix.InitScale (1, -1);
- matrix.Translate (0, -(2 * pixbuf.Height) + 1);
+ matrix.Translate (0, -(2 * image.Height) + 1);
cr.Transform (matrix);
- cr.SetSource (img);
+ cr.SetSource (image);
cr.Paint ();
cr.Restore ();
@@ -142,16 +141,14 @@
Color bg_transparent = BackgroundColor;
bg_transparent.A = 0.65;
- LinearGradient mask = new LinearGradient (0, pixbuf.Height, 0, pixbuf.Height + reflect);
+ LinearGradient mask = new LinearGradient (0, image.Height, 0, image.Height + reflect);
mask.AddColorStop (0, bg_transparent);
mask.AddColorStop (1, BackgroundColor);
- cr.Rectangle (0, pixbuf.Height, pixbuf.Width, reflect);
+ cr.Rectangle (0, image.Height, image.Width, reflect);
cr.Pattern = mask;
cr.Fill ();
- img.Destroy ();
-
((IDisposable)cr).Dispose ();
return surface;
}
@@ -236,7 +233,7 @@
protected override void Invalidate ()
{
- if (CurrentPixbuf == null || CurrentTrack == null || IncomingPixbuf == null || IncomingTrack == null) {
+ if (CurrentImage == null || CurrentTrack == null || IncomingImage == null || IncomingTrack == null) {
QueueDraw ();
} else {
Gdk.Rectangle alloc = RenderAllocation;
@@ -245,5 +242,14 @@
alloc.Width - text_alloc.Width - Spacing, alloc.Height);
}
}
+
+ protected override void InvalidateCache ()
+ {
+ foreach (Surface surface in surfaces.Values) {
+ surface.Destroy ();
+ }
+
+ surfaces.Clear ();
+ }
}
}
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TrackInfoDisplay.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TrackInfoDisplay.cs (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TrackInfoDisplay.cs Mon Oct 6 21:04:57 2008
@@ -32,7 +32,7 @@
using Mono.Unix;
using Gtk;
-using Gdk;
+using Cairo;
using Hyena;
using Hyena.Gui;
@@ -53,24 +53,26 @@
get { return artwork_manager; }
}
- private Pixbuf current_pixbuf;
- protected Pixbuf CurrentPixbuf {
- get { return current_pixbuf; }
+ private ImageSurface current_image;
+ protected ImageSurface CurrentImage {
+ get { return current_image; }
}
- private Pixbuf incoming_pixbuf;
- protected Pixbuf IncomingPixbuf {
- get { return incoming_pixbuf; }
+ private ImageSurface incoming_image;
+ protected ImageSurface IncomingImage {
+ get { return incoming_image; }
}
- private Pixbuf missing_audio_pixbuf;
- protected Pixbuf MissingAudioPixbuf {
- get { return missing_audio_pixbuf ?? missing_audio_pixbuf = IconThemeUtils.LoadIcon (MissingIconSizeRequest, "audio-x-generic"); }
+ private ImageSurface missing_audio_image;
+ protected ImageSurface MissingAudioImage {
+ get { return missing_audio_image ?? missing_audio_image
+ = new PixbufImageSurface (IconThemeUtils.LoadIcon (MissingIconSizeRequest, "audio-x-generic"), true); }
}
- private Pixbuf missing_video_pixbuf;
- protected Pixbuf MissingVideoPixbuf {
- get { return missing_video_pixbuf ?? missing_video_pixbuf = IconThemeUtils.LoadIcon (MissingIconSizeRequest, "video-x-generic"); }
+ private ImageSurface missing_video_image;
+ protected ImageSurface MissingVideoImage {
+ get { return missing_video_image ?? missing_video_image
+ = new PixbufImageSurface (IconThemeUtils.LoadIcon (MissingIconSizeRequest, "video-x-generic"), true); }
}
private Cairo.Color background_color;
@@ -100,8 +102,7 @@
private uint idle_timeout_id = 0;
private SingleActorStage stage = new SingleActorStage ();
- private Dictionary<Pixbuf, Cairo.Surface> surface_cache = new Dictionary<Pixbuf, Cairo.Surface> ();
-
+
protected TrackInfoDisplay (IntPtr native) : base (native)
{
}
@@ -135,7 +136,7 @@
stage.Iteration -= OnStageIteration;
stage = null;
- SurfaceCacheFlush ();
+ InvalidateCache ();
base.Dispose ();
}
@@ -149,17 +150,17 @@
protected override void OnUnrealized ()
{
base.OnUnrealized ();
- SurfaceCacheFlush ();
+ InvalidateCache ();
}
- protected override void OnSizeAllocated (Rectangle allocation)
+ protected override void OnSizeAllocated (Gdk.Rectangle allocation)
{
base.OnSizeAllocated (allocation);
if (current_track == null) {
LoadCurrentTrack ();
} else {
- LoadPixbuf (current_track);
+ LoadImage (current_track);
}
}
@@ -171,18 +172,18 @@
background_color = CairoExtensions.GdkColorToCairoColor (Style.Background (StateType.Normal));
text_light_color = Hyena.Gui.Theming.GtkTheme.GetCairoTextMidColor (this);
- if (missing_audio_pixbuf != null) {
- missing_audio_pixbuf.Dispose ();
- missing_audio_pixbuf = null;
+ if (missing_audio_image != null) {
+ missing_audio_image.Destroy ();
+ missing_audio_image = null;
}
- if (missing_video_pixbuf != null) {
- missing_video_pixbuf.Dispose ();
- missing_video_pixbuf = null;
+ if (missing_video_image != null) {
+ missing_video_image.Destroy ();
+ missing_video_image = null;
}
}
- protected override bool OnExposeEvent (EventExpose evnt)
+ protected override bool OnExposeEvent (Gdk.EventExpose evnt)
{
bool idle = incoming_track == null && current_track == null;
if (!Visible || !IsMapped || (idle && !CanRenderIdle)) {
@@ -221,14 +222,14 @@
{
if (stage.Actor == null) {
// We are not in a transition, just render
- RenderStage (cr, current_track, current_pixbuf);
+ RenderStage (cr, current_track, current_image);
return;
}
if (current_track == null) {
// Fade in the whole stage, nothing to fade out
CairoExtensions.PushGroup (cr);
- RenderStage (cr, incoming_track, incoming_pixbuf);
+ RenderStage (cr, incoming_track, incoming_image);
CairoExtensions.PopGroupToSource (cr);
cr.PaintWithAlpha (stage.Actor.Percent);
@@ -236,10 +237,10 @@
}
// XFade only the cover art
- RenderCoverArt (cr, incoming_pixbuf);
+ RenderCoverArt (cr, incoming_image);
CairoExtensions.PushGroup (cr);
- RenderCoverArt (cr, current_pixbuf);
+ RenderCoverArt (cr, current_image);
CairoExtensions.PopGroupToSource (cr);
cr.PaintWithAlpha (1.0 - stage.Actor.Percent);
@@ -269,23 +270,27 @@
}
}
- private void RenderStage (Cairo.Context cr, TrackInfo track, Pixbuf pixbuf)
+ private void RenderStage (Cairo.Context cr, TrackInfo track, ImageSurface image)
{
- RenderCoverArt (cr, pixbuf);
+ RenderCoverArt (cr, image);
RenderTrackInfo (cr, track, true, true);
}
- protected virtual void RenderCoverArt (Cairo.Context cr, Pixbuf pixbuf)
+ protected virtual void RenderCoverArt (Cairo.Context cr, ImageSurface image)
{
- ArtworkRenderer.RenderThumbnail (cr, pixbuf, false, Allocation.X, Allocation.Y,
+ ArtworkRenderer.RenderThumbnail (cr, image, false, Allocation.X, Allocation.Y,
ArtworkSizeRequest, ArtworkSizeRequest,
- !IsMissingPixbuf (pixbuf), 0,
- IsMissingPixbuf (pixbuf), BackgroundColor);
+ !IsMissingImage (image), 0,
+ IsMissingImage (image), BackgroundColor);
}
- protected bool IsMissingPixbuf (Pixbuf pb)
+ protected bool IsMissingImage (ImageSurface pb)
+ {
+ return pb == missing_audio_image || pb == missing_video_image;
+ }
+
+ protected virtual void InvalidateCache ()
{
- return (pb == missing_audio_pixbuf || pb == missing_video_pixbuf);
}
protected abstract void RenderTrackInfo (Cairo.Context cr, TrackInfo track, bool renderTrack, bool renderArtistAlbum);
@@ -302,7 +307,7 @@
{
if (args.Event == PlayerEvent.StartOfStream || args.Event == PlayerEvent.TrackInfoUpdated) {
LoadCurrentTrack ();
- } else if (args.Event == PlayerEvent.StateChange && (incoming_track != null || incoming_pixbuf != null)) {
+ } else if (args.Event == PlayerEvent.StateChange && (incoming_track != null || incoming_image != null)) {
PlayerEventStateChangeArgs state = (PlayerEventStateChangeArgs)args;
if (state.Current == PlayerState.Idle) {
if (idle_timeout_id > 0) {
@@ -319,7 +324,7 @@
if (ServiceManager.PlayerEngine.CurrentTrack == null ||
ServiceManager.PlayerEngine.CurrentState == PlayerState.Idle) {
incoming_track = null;
- incoming_pixbuf = null;
+ incoming_image = null;
if (stage != null && stage.Actor == null) {
stage.Reset ();
@@ -334,41 +339,41 @@
{
TrackInfo track = ServiceManager.PlayerEngine.CurrentTrack;
- if (track == current_track && !IsMissingPixbuf (current_pixbuf)) {
+ if (track == current_track && !IsMissingImage (current_image)) {
return;
} else if (track == null) {
incoming_track = null;
- incoming_pixbuf = null;
+ incoming_image = null;
return;
}
incoming_track = track;
- LoadPixbuf (track);
+ LoadImage (track);
if (stage.Actor == null) {
stage.Reset ();
}
}
- private void LoadPixbuf (TrackInfo track)
+ private void LoadImage (TrackInfo track)
{
- Gdk.Pixbuf pixbuf = artwork_manager.LookupScale (track.ArtworkId, ArtworkSizeRequest);
+ ImageSurface image = artwork_manager.LookupScaleSurface (track.ArtworkId, ArtworkSizeRequest);
- if (pixbuf == null) {
- LoadMissingPixbuf ((track.MediaAttributes & TrackMediaAttributes.VideoStream) != 0);
+ if (image == null) {
+ LoadMissingImage ((track.MediaAttributes & TrackMediaAttributes.VideoStream) != 0);
} else {
- incoming_pixbuf = pixbuf;
+ incoming_image = image;
}
if (track == current_track) {
- current_pixbuf = incoming_pixbuf;
+ current_image = incoming_image;
}
}
- private void LoadMissingPixbuf (bool is_video)
+ private void LoadMissingImage (bool is_video)
{
- incoming_pixbuf = is_video ? MissingVideoPixbuf : MissingAudioPixbuf;
+ incoming_image = is_video ? MissingVideoImage : MissingAudioImage;
}
private double last_fps = 0.0;
@@ -382,17 +387,17 @@
return;
}
- SurfaceCacheFlush ();
+ InvalidateCache ();
if (ApplicationContext.Debugging) {
Log.DebugFormat ("TrackInfoDisplay RenderAnimation: {0:0.00} FPS", last_fps);
}
- if (current_pixbuf != incoming_pixbuf && !IsMissingPixbuf (current_pixbuf)) {
- ArtworkRenderer.DisposePixbuf (current_pixbuf);
+ if (current_image != incoming_image && !IsMissingImage (current_image)) {
+ current_image.Destroy ();
}
- current_pixbuf = incoming_pixbuf;
+ current_image = incoming_image;
current_track = incoming_track;
incoming_track = null;
@@ -490,46 +495,5 @@
}
return markup;
}
-
- protected void SurfaceExpire (Gdk.Pixbuf pixbuf)
- {
- if (pixbuf == null) {
- return;
- }
-
- Cairo.Surface surface = null;
- if (surface_cache.TryGetValue (pixbuf, out surface)) {
- surface.Destroy ();
- surface_cache.Remove (pixbuf);
- }
- }
-
- protected void SurfaceCacheFlush ()
- {
- foreach (Cairo.Surface surface in surface_cache.Values) {
- surface.Destroy ();
- }
-
- surface_cache.Clear ();
- }
-
- protected void SurfaceCache (Gdk.Pixbuf pixbuf, Cairo.Surface surface)
- {
- if (pixbuf == null || surface == null) {
- return;
- }
-
- SurfaceExpire (pixbuf);
- surface_cache.Add (pixbuf, surface);
- }
-
- protected Cairo.Surface SurfaceLookup (Gdk.Pixbuf pixbuf)
- {
- Cairo.Surface surface = null;
- if (pixbuf != null) {
- surface_cache.TryGetValue (pixbuf, out surface);
- }
- return surface;
- }
}
}
Modified: trunk/banshee/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs
==============================================================================
--- trunk/banshee/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs (original)
+++ trunk/banshee/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs Mon Oct 6 21:04:57 2008
@@ -458,10 +458,10 @@
SafeUri local_cover_uri = new SafeUri (Banshee.Base.CoverArtSpec.GetPath (coverart_id));
Banshee.IO.File.Copy (local_cover_uri, cover_uri, false);
} else {
- pic = artwork_manager.Lookup (coverart_id);
+ pic = artwork_manager.LookupPixbuf (coverart_id);
}
} else {
- pic = artwork_manager.LookupScale (coverart_id, CoverArtSize);
+ pic = artwork_manager.LookupScalePixbuf (coverart_id, CoverArtSize);
}
if (pic != null) {
@@ -473,7 +473,7 @@
} catch (GLib.GException){
Log.DebugFormat ("Could convert cover art to {0}, unsupported filetype?", CoverArtFileType);
} finally {
- pic.Dispose ();
+ Banshee.Collection.Gui.ArtworkManager.DisposePixbuf (pic);
}
}
}
Modified: trunk/banshee/src/Dap/Banshee.Dap.Mtp/Banshee.Dap.Mtp/MtpSource.cs
==============================================================================
--- trunk/banshee/src/Dap/Banshee.Dap.Mtp/Banshee.Dap.Mtp/MtpSource.cs (original)
+++ trunk/banshee/src/Dap/Banshee.Dap.Mtp/Banshee.Dap.Mtp/MtpSource.cs Mon Oct 6 21:04:57 2008
@@ -320,13 +320,13 @@
if (supports_jpegs && can_sync) {
try {
- Gdk.Pixbuf pic = ServiceManager.Get<Banshee.Collection.Gui.ArtworkManager> ().LookupScale (
+ Gdk.Pixbuf pic = ServiceManager.Get<Banshee.Collection.Gui.ArtworkManager> ().LookupScalePixbuf (
track.ArtworkId, thumb_width
);
if (pic != null) {
byte [] bytes = pic.SaveToBuffer ("jpeg");
album.Save (bytes, (uint)pic.Width, (uint)pic.Height);
- pic.Dispose ();
+ Banshee.Collection.Gui.ArtworkManager.DisposePixbuf (pic);
}
album_cache[key] = album;
} catch {}
Modified: trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs (original)
+++ trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs Mon Oct 6 21:04:57 2008
@@ -226,7 +226,7 @@
AlbumInfo album = new AlbumInfo (track.Album);
album.ArtistName = track.Artist;
- Gdk.Pixbuf pb = artwork_manager == null ? null : artwork_manager.LookupScale (album.ArtworkId, 40);
+ Gdk.Pixbuf pb = artwork_manager == null ? null : artwork_manager.LookupScalePixbuf (album.ArtworkId, 40);
if (pb != null) {
tile.Pixbuf = pb;
}
Modified: trunk/banshee/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs (original)
+++ trunk/banshee/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs Mon Oct 6 21:04:57 2008
@@ -386,7 +386,7 @@
Gdk.Pixbuf image = null;
if (artwork_manager_service != null) {
- image = artwork_manager_service.LookupScale (current_track.ArtworkId, 42);
+ image = artwork_manager_service.LookupScalePixbuf (current_track.ArtworkId, 42);
}
if (image == null) {
Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcast.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcast.cs (original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcast.cs Mon Oct 6 21:04:57 2008
@@ -46,11 +46,12 @@
{
public class ColumnCellPodcast : ColumnCell
{
- private static int pixbuf_spacing = 4;
- private static int pixbuf_size = 48;
+ private static int image_spacing = 4;
+ private static int image_size = 48;
// TODO replace this w/ new icon installation etc
- private static Gdk.Pixbuf default_cover_pixbuf = IconThemeUtils.LoadIcon (48, "podcast");
+ private static ImageSurface default_cover_image
+ = new PixbufImageSurface (IconThemeUtils.LoadIcon (48, "podcast"));
private ArtworkManager artwork_manager;
@@ -72,20 +73,21 @@
Feed feed = (Feed)BoundObject;
bool is_default = false;
- Gdk.Pixbuf pixbuf = artwork_manager == null ? null : artwork_manager.LookupScale (PodcastService.ArtworkIdFor (feed), pixbuf_size);
+ ImageSurface image = artwork_manager == null ? null
+ : artwork_manager.LookupScaleSurface (PodcastService.ArtworkIdFor (feed), image_size, true);
- if (pixbuf == null) {
- pixbuf = default_cover_pixbuf;
+ if (image == null) {
+ image = default_cover_image;
is_default = true;
}
- // int pixbuf_render_size = is_default ? pixbuf.Height : (int)cellHeight - 8;
- int pixbuf_render_size = pixbuf_size;
- int x = pixbuf_spacing;
- int y = ((int)cellHeight - pixbuf_render_size) / 2;
+ // int image_render_size = is_default ? image.Height : (int)cellHeight - 8;
+ int image_render_size = image_size;
+ int x = image_spacing;
+ int y = ((int)cellHeight - image_render_size) / 2;
- ArtworkRenderer.RenderThumbnail (context.Context, pixbuf, !is_default, x, y,
- pixbuf_render_size, pixbuf_render_size, !is_default, context.Theme.Context.Radius);
+ ArtworkRenderer.RenderThumbnail (context.Context, image, !is_default, x, y,
+ image_render_size, image_render_size, !is_default, context.Theme.Context.Radius);
int fl_width = 0, fl_height = 0, sl_width = 0, sl_height = 0;
Cairo.Color text_color = context.Theme.Colors.GetWidgetColor (GtkColorClass.Text, state);
@@ -164,7 +166,7 @@
layout.Dispose ();
- return (height < pixbuf_size ? pixbuf_size : height) + 6;
+ return (height < image_size ? image_size : height) + 6;
}
}
}
Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui/PixbufImageSurface.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui/PixbufImageSurface.cs (original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui/PixbufImageSurface.cs Mon Oct 6 21:04:57 2008
@@ -53,24 +53,32 @@
private IntPtr data;
- public PixbufImageSurface (Gdk.Pixbuf pixbuf) : this (pixbuf.Width, pixbuf.Height,
- pixbuf.NChannels, pixbuf.Rowstride, pixbuf.Pixels)
+ public PixbufImageSurface (Gdk.Pixbuf pixbuf) : this (pixbuf, false)
+ {
+ }
+
+ public PixbufImageSurface (Gdk.Pixbuf pixbuf, bool disposePixbuf) : this (disposePixbuf ? pixbuf : null,
+ pixbuf.Width, pixbuf.Height, pixbuf.NChannels, pixbuf.Rowstride, pixbuf.Pixels)
{
}
// This ctor is to avoid multiple queries against the GdkPixbuf for width/height
- private PixbufImageSurface (int width, int height, int channels, int rowstride, IntPtr pixels) : this (
- Marshal.AllocHGlobal (width * height * 4), width, height, channels, rowstride, pixels)
+ private PixbufImageSurface (Gdk.Pixbuf pixbuf, int width, int height, int channels, int rowstride, IntPtr pixels)
+ : this (pixbuf, Marshal.AllocHGlobal (width * height * 4), width, height, channels, rowstride, pixels)
{
}
- private PixbufImageSurface (IntPtr data, int width, int height, int channels, int rowstride, IntPtr pixels)
+ private PixbufImageSurface (Gdk.Pixbuf pixbuf, IntPtr data, int width, int height, int channels, int rowstride, IntPtr pixels)
: base (data, channels == 3 ? Format.Rgb24 : Format.Argb32, width, height, width * 4)
{
this.data = data;
CreateSurface (width, height, channels, rowstride, pixels);
SetDestroyFunc ();
+
+ if (pixbuf != null && pixbuf.Handle != IntPtr.Zero) {
+ pixbuf.Dispose ();
+ }
}
private unsafe void CreateSurface (int width, int height, int channels, int gdk_rowstride, IntPtr pixels)
Added: trunk/banshee/src/Libraries/Hyena/Hyena.Collections/LruCache.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Collections/LruCache.cs Mon Oct 6 21:04:57 2008
@@ -0,0 +1,167 @@
+//
+// LruCache.cs
+//
+// Author:
+// Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Hyena.Collections
+{
+ public struct CacheEntry<TKey, TValue>
+ {
+ private TKey key;
+ public TKey Key {
+ get { return key; }
+ set { key = value; }
+ }
+
+ private TValue value;
+ public TValue Value {
+ get { return this.value; }
+ set { this.value = value; }
+ }
+
+ internal DateTime LastUsed;
+ internal int UsedCount;
+ }
+
+ public class LruCache<TKey, TValue> : IEnumerable<CacheEntry<TKey, TValue>>
+ {
+ private Dictionary<TKey, CacheEntry<TKey, TValue>> cache;
+ private int max_count;
+
+ public LruCache () : this (1024)
+ {
+ }
+
+ public LruCache (int maxCount)
+ {
+ max_count = maxCount;
+ cache = new Dictionary<TKey, CacheEntry<TKey, TValue>> ();
+ }
+
+ public void Add (TKey key, TValue value)
+ {
+ lock (cache) {
+ CacheEntry<TKey, TValue> entry;
+ if (cache.TryGetValue (key, out entry)) {
+ Ref (ref entry);
+ cache[key] = entry;
+ return;
+ }
+
+ entry.Key = key;
+ entry.Value = value;
+ Ref (ref entry);
+ cache.Add (key, entry);
+
+ if (Count >= max_count) {
+ TKey expire = FindOldestEntry ();
+ ExpireItem (cache[expire].Value);
+ cache.Remove (expire);
+ }
+ }
+ }
+
+ public bool Contains (TKey key)
+ {
+ lock (cache) {
+ return cache.ContainsKey (key);
+ }
+ }
+
+ public bool TryGetValue (TKey key, out TValue value)
+ {
+ lock (cache) {
+ CacheEntry<TKey, TValue> entry;
+ if (cache.TryGetValue (key, out entry)) {
+ value = entry.Value;
+ Ref (ref entry);
+ cache[key] = entry;
+ return true;
+ }
+
+ value = default (TValue);
+ return false;
+ }
+ }
+
+ private void Ref (ref CacheEntry<TKey, TValue> entry)
+ {
+ entry.LastUsed = DateTime.Now;
+ entry.UsedCount++;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return GetEnumerator ();
+ }
+
+ public IEnumerator<CacheEntry<TKey, TValue>> GetEnumerator ()
+ {
+ lock (cache) {
+ foreach (KeyValuePair<TKey, CacheEntry<TKey, TValue>> item in cache) {
+ yield return item.Value;
+ }
+ }
+ }
+
+ // Ok, this blows. I have no time to implement anything clever or proper here.
+ // Using a hashtable generally sucks for this, but it's not bad for a 15 minute
+ // hack. max_count will be sufficiently small in our case that this can't be
+ // felt anyway. Meh.
+
+ private TKey FindOldestEntry ()
+ {
+ lock (cache) {
+ DateTime oldest = DateTime.Now;
+ TKey oldest_key = default (TKey);
+ foreach (CacheEntry<TKey, TValue> item in this) {
+ if (item.LastUsed < oldest) {
+ oldest = item.LastUsed;
+ oldest_key = item.Key;
+ }
+ }
+ return oldest_key;
+ }
+ }
+
+ protected virtual void ExpireItem (TValue item)
+ {
+ }
+
+ public int MaxCount {
+ get { lock (cache) { return max_count; } }
+ set { lock (cache) { max_count = value; } }
+ }
+
+ public int Count {
+ get { lock (cache) { return cache.Count; } }
+ }
+ }
+}
Modified: trunk/banshee/src/Libraries/Hyena/Hyena.csproj
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.csproj (original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.csproj Mon Oct 6 21:04:57 2008
@@ -129,6 +129,7 @@
<Compile Include="Hyena.Json\IJsonCollection.cs" />
<Compile Include="Hyena.Json\Tests\DeserializerTests.cs" />
<Compile Include="Hyena\Delegates.cs" />
+ <Compile Include="Hyena.Collections\LruCache.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
Modified: trunk/banshee/src/Libraries/Hyena/Makefile.am
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Makefile.am (original)
+++ trunk/banshee/src/Libraries/Hyena/Makefile.am Mon Oct 6 21:04:57 2008
@@ -5,6 +5,7 @@
Hyena.Collections/CollectionExtensions.cs \
Hyena.Collections/IntervalHeap.cs \
Hyena.Collections/IStackProvider.cs \
+ Hyena.Collections/LruCache.cs \
Hyena.Collections/QueuePipeline.cs \
Hyena.Collections/QueuePipelineElement.cs \
Hyena.Collections/RangeCollection.cs \
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]