- From a664d1c72c389caa3ea6cb67c50a039380cf2555 Mon Sep 17 00:00:00 2001
- From: Till Hartmann <ich@till-hartmann.de>
- Date: Sun, 25 Jul 2010 14:58:44 +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 | 91 +++++++++++++++++++++++++++++++++++++++++---------
- 2 files changed, 82 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..767c845 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,79 @@ 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 = None
- + 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'
- + file_mtime = str(int(os.stat(filename).st_mtime))
- + if (not os.path.isfile(tb_filename_normal)) or (not pixbuf) or (file_mtime != pixbuf.get_option("tEXt::Thumb::MTime")):
- + pixbuf = get_pixbuf(filename)
- + if pixbuf:
- + pixbuf = scale_proportionally(pixbuf, 128,128)
- + pixbuf.save(tb_filename_normal, 'png', {"tEXt::Thumb::MTime" : file_mtime, "tEXt::Thumb::URI" : ('file://'+filename)})
- + return pixbuf
- + else:
- + return pixbuf
- +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