Hi Martin

Thanks for having a look at my patches.

On 17.07.2010 12:25, Martin Renold wrote:
> It looks good to me and as soon as
> http://jens.triq.net/thumbnail-spec/modifications.html
> is also implemented this can go into master.
> 
Both options (key/value pairs), "Thumb::MTime" (to check for
modification time) and "Thumb::URI" get saved now.
> Below are only some minor points.
> 
> - One of the commit messages has a too long summary (just add two newlines).
> 
Reverted to master and reworked the functionality anway, so attached
will be only a single patch this time.
> - I think the guideline asks us to check for a 256x256 thumbnail before we
>   start to generate a 128x128 one.  Would be nice to have this.
> 
It will now check for a 128px² thumbnail (as that is the preview size)
first; if it doesn't exist it will check for a 256px² thumbnail. If
that, too, doesn't exist, it will simply try to load the file (if it is
an image) directly. (save_freedesktop_thumbnail will get called in
either case. Hm, now that I think of it, calling the method
"get_freedesktop_thumbnail" might be a bit misleading, as it does not
only get but also create thumbnails.)
> - As I said on IRC, no point to safe only .ora thumbnails I think.
> 
Everything gtk/gdk can handle plus .ora files will be thumbnailed now.

>> +        else:
>> +            pass
> 
> Ideally we would set some "broken thumbnail" image, or just all-blank.
> 
Yes, you'd just have to tell me where to include/place a
no-preview-available.png (I see there is a theme search path in
application.py for the icons etc?).

Till


>From 2f8f556de20641cc1b74f90932002867102f988f Mon Sep 17 00:00:00 2001
From: Till Hartmann <[email protected]>
Date: Sun, 25 Jul 2010 00:13:17 +0200
Subject: [PATCH] Thumbnail storage and retrieval now follow FDO specs.
 See http://jens.triq.net/thumbnail-spec/introduction.html for the
 complete freedesktop.org thumbnail specification.

---
 gui/filehandling.py |   31 +++++-------------
 lib/helpers.py      |   87 +++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 78 insertions(+), 40 deletions(-)

diff --git a/gui/filehandling.py b/gui/filehandling.py
index 871f071..336727b 100644
--- a/gui/filehandling.py
+++ b/gui/filehandling.py
@@ -17,7 +17,6 @@ from gettext import ngettext
 from lib import document, helpers
 import drawwindow
 
-import zipfile
 import mimetypes
 
 SAVE_FORMAT_ANY = 0
@@ -253,30 +252,16 @@ class FileHandler(object):
 
     def update_preview_cb(self, file_chooser, preview):
         filename = file_chooser.get_preview_filename()
-        pixbuf = self.get_preview_image(filename)
-        preview.set_from_pixbuf(pixbuf)
-        file_chooser.set_preview_widget_active(pixbuf != None)
-
-    def get_preview_image(self, filename):
         if filename:
-            if os.path.splitext(filename)[1].lower() == ".ora":
-                ora = zipfile.ZipFile(file(filename))
-                try:
-                    data = ora.read("Thumbnails/thumbnail.png")
-                except KeyError:
-                    return None
-                loader = gtk.gdk.PixbufLoader("png")
-                loader.write(data)
-                loader.close()
-                pixbuf = loader.get_pixbuf()
-                return pixbuf
+            pixbuf = helpers.get_freedesktop_thumbnail(filename)
+            if pixbuf:
+                # if pixbuf is smaller than 128px in width, copy it onto a transparent 128x128 pixbuf
+                pixbuf = helpers.pixbuf_thumbnail(pixbuf, 128, 128, True)
+                preview.set_from_pixbuf(pixbuf)
+                file_chooser.set_preview_widget_active(True)
             else:
-                try:
-                    #TODO do not scale images smaller than 256x256 up.
-                    pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filename, 256, 256)
-                    return pixbuf
-                except:
-                    pass
+                #TODO display "no preview available" image
+                pass
 
     def open_cb(self, action):
         if not self.confirm_destructive_action():
diff --git a/lib/helpers.py b/lib/helpers.py
index f6ca5af..335c5f6 100644
--- a/lib/helpers.py
+++ b/lib/helpers.py
@@ -12,6 +12,10 @@ import colorsys, urllib, gc
 from gtk import gdk # for gdk_pixbuf stuff
 import mypaintlib
 
+import hashlib
+import os
+import zipfile
+
 try:
     from json import dumps as json_dumps, loads as json_loads
     print "builtin python 2.6 json support"
@@ -111,26 +115,75 @@ def gdkpixbuf2numpy(pixbuf):
     arr = pixbuf.get_pixels_array()
     return mypaintlib.gdkpixbuf_numeric2numpy(arr)
 
-def pixbuf_thumbnail(src, w, h):
+def get_freedesktop_thumbnail(filename):
     """
-    Creates a centered thumbnail of a gdk.pixbuf.
+    Tries to fetch a thumbnail from ~/.thumbnails.
+    If there is no thumbnail for the specified filename,
+    a new thumbnail will be generated and stored according to the FDO spec.
+    A thumbnail will also get regenerated if the MTimes (as in "modified")
+    of thumbnail and original image do not match.
     """
-    src_w = src.get_width()
-    src_h = src.get_height()
-
-    w2, h2 = src_w, src_h
-    if w2 > w:
-        h2 = h2*w/w2
-        w2 = w
-    if h2 > h:
-        w2 = w2*h/h2
-        h2 = h
-    assert w2 <= w and h2 <= h
-    src2 = src.scale_simple(w2, h2, gdk.INTERP_BILINEAR)
-    
-    dst = gdk.Pixbuf(gdk.COLORSPACE_RGB, False, 8, w, h)
-    dst.fill(0xffffffff) # white background
+    file_hash = hashlib.md5('file://'+filename).hexdigest()
+    tb_filename_normal = os.path.join(os.path.expanduser('~/.thumbnails/normal'), file_hash) + '.png'
+    tb_filename_large = os.path.join(os.path.expanduser('~/.thumbnails/large'), file_hash) + '.png'
+    if os.path.isfile(tb_filename_normal):
+        pixbuf = gdk.pixbuf_new_from_file(tb_filename_normal)
+    elif os.path.isfile(tb_filename_large):
+        pixbuf = gdk.pixbuf_new_from_file(tb_filename_large)
+    else:
+        pixbuf = get_pixbuf(filename)
+    if pixbuf:
+        save_freedesktop_thumbnail(pixbuf, filename) # save thumbnail or regenerate if MTimes do not match
+    return pixbuf
+
+def save_freedesktop_thumbnail(pixbuf, filename):
+    """
+    Saves a thumbnail according to the FDO spec.
+    """
+    file_hash = hashlib.md5('file://'+filename).hexdigest()
+    tb_filename_normal = os.path.join(os.path.expanduser('~/.thumbnails/normal'), file_hash) + '.png'
+    if (not os.path.isfile(tb_filename_normal)) or (str(os.stat(filename).st_mtime) != pixbuf.get_option("tEXt::Thumb::MTime")):
+        pixbuf = scale_proportionally(pixbuf, 128,128)
+        #print ("recreated thumbnail for "+filename+" modified: " + str(os.stat(filename).st_mtime))
+        pixbuf.save(tb_filename_normal, 'png', {"tEXt::Thumb::MTime" : str(os.stat(filename).st_mtime), "tEXt::Thumb::URI" : ('file://'+filename)})
 
+def get_pixbuf(filename):
+    try:
+        if os.path.splitext(filename)[1].lower() == ".ora":
+            ora = zipfile.ZipFile(file(filename))
+            data = ora.read("Thumbnails/thumbnail.png")
+            loader = gdk.PixbufLoader("png")
+            loader.write(data)
+            loader.close()
+            pixbuf = loader.get_pixbuf()
+            return pixbuf
+        else:
+            pixbuf = gdk.pixbuf_new_from_file(filename)
+            return pixbuf;
+    except:
+        pass
+
+def scale_proportionally(pixbuf, w, h, shrink_only=True):
+    width, height = pixbuf.get_width(), pixbuf.get_height()
+    scale = min(w / float(width), h / float(height))
+    if shrink_only and scale >= 1:
+        return pixbuf
+    new_width, new_height = int(width * scale), int(height * scale)
+    if new_width > 0 and new_height > 0:
+        pixbuf = pixbuf.scale_simple(new_width, new_height, gdk.INTERP_BILINEAR)
+    return pixbuf
+
+def pixbuf_thumbnail(src, w, h, alpha=False):
+    """
+    Creates a centered thumbnail of a gdk.pixbuf.
+    """
+    src2 = scale_proportionally(src, w, h)
+    w2, h2 = src2.get_width(), src2.get_height()
+    dst = gdk.Pixbuf(gdk.COLORSPACE_RGB, alpha, 8, w, h)
+    if alpha:
+        dst.fill(0xffffff00) # transparent background
+    else:
+        dst.fill(0xffffffff) # white background
     src2.copy_area(0, 0, w2, h2, dst, (w-w2)/2, (h-h2)/2)
     return dst
 
-- 
1.7.0.4

_______________________________________________
Mypaint-discuss mailing list
[email protected]
https://mail.gna.org/listinfo/mypaint-discuss

Reply via email to