diff options
Diffstat (limited to 'port/pixbuf.py')
-rw-r--r-- | port/pixbuf.py | 179 |
1 files changed, 171 insertions, 8 deletions
diff --git a/port/pixbuf.py b/port/pixbuf.py index 4390fef..ba6d0f5 100644 --- a/port/pixbuf.py +++ b/port/pixbuf.py @@ -14,10 +14,16 @@ """gtk.gdk.Pixbuf extensions""" +import re import os import cStringIO -import tempfile import gtk +import rsvg +import cairo +import logging + +from sugar.graphics.xocolor import XoColor +from sugar.util import LRU def to_file(pixbuf): """Convert pixbuf object to file object""" @@ -38,13 +44,170 @@ def to_str(pixbuf): def from_str(str): """Convert string to pixbuf object""" - file_d, path = tempfile.mkstemp() + loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/png') + loader.write(str) + loader.close() + + return loader.get_pixbuf() + +def from_svg_at_size(filename=None, width=None, height=None, handle=None, + keep_ratio=True): + """Scale and load SVG into pixbuf""" + + if not handle: + handle = rsvg.Handle(filename) + + dimensions = handle.get_dimension_data() + icon_width = dimensions[0] + icon_height = dimensions[1] + + if icon_width != width or icon_height != height: + ratio_width = float(width) / icon_width + ratio_height = float(height) / icon_height + + if keep_ratio: + ratio = min(ratio_width, ratio_height) + if ratio_width != ratio: + ratio_width = ratio + width = int(icon_width * ratio) + elif ratio_height != ratio: + ratio_height = ratio + height = int(icon_height * ratio) + else: + ratio_width = 1 + ratio_height = 1 + + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + context = cairo.Context(surface) + context.scale(ratio_width, ratio_height) + handle.render_cairo(context) + + loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/png') + surface.write_to_png(loader) + loader.close() + + return loader.get_pixbuf() + +def sugar_icon(file_name=None, icon_name=None, + width=None, height=None, + color=None, + insensitive_widget=None): + """Load sugar icon into pixbuf + + NOTE: Function can load all image formats but makes sense only for SVG + (due to color argument, see load_svg()) + + NOTE: Function caches results + + Arguments: + file_name path to filename with image + (mutually exclusive for icon_name) + icon_name name of icon + (mutually exclusive for icon_name) + width width of final image + height height of final image + color defines stroke and fill colors for final SVG image + in string notion, could be: + * tuple of (stroke_color, fill_color) + * XoColor + * scalar value for stroke and fill colors + insensitive_widget render icon in insensitive mode + """ + def load_svg(): + entities = {} + if fill_color: + entities['fill_color'] = fill_color + if stroke_color: + entities['stroke_color'] = stroke_color + + f = open(icon_filename, 'r') + icon = f.read() + f.close() + + for entity, value in entities.items(): + xml = '<!ENTITY %s "%s">' % (entity, value) + icon = re.sub('<!ENTITY %s .*>' % entity, xml, icon) + + return rsvg.Handle(data=icon) + + def get_insensitive_pixbuf(): + if not (insensitive_widget and insensitive_widget.style): + return pixbuf + + icon_source = gtk.IconSource() + # Special size meaning "don't touch" + icon_source.set_size(-1) + icon_source.set_pixbuf(pixbuf) + icon_source.set_state(gtk.STATE_INSENSITIVE) + icon_source.set_direction_wildcarded(False) + icon_source.set_size_wildcarded(False) + + # Please note that the pixbuf returned by this function is leaked + # with current stable versions of pygtk. The relevant bug is + # http://bugzilla.gnome.org/show_bug.cgi?id=502871 + # -- 2007-12-14 Benjamin Berg + pixbuf = insensitive_widget.style.render_icon(icon_source, + insensitive_widget.get_direction(), gtk.STATE_INSENSITIVE, -1, + insensitive_widget, "sugar-icon") + + return pixbuf + + def get_cache_key(): + return (icon_filename, fill_color, stroke_color, width, height, + insensitive_widget is None) + + if isinstance(color, XoColor): + stroke_color = color.get_stroke_color() + fill_color = color.get_fill_color() + elif isinstance(color, tuple): + stroke_color = color[0] + fill_color = color[1] + else: + stroke_color = color + fill_color = color + + if file_name: + icon_filename = file_name + elif icon_name: + theme = gtk.icon_theme_get_default() + info = theme.lookup_icon(icon_name, width or 50, 0) + if info: + icon_filename = info.get_filename() + del info + else: + logging.warning('No icon with the name %s ' + 'was found in the theme.' % icon_name) + else: + return None + + cache_key = get_cache_key() + if cache_key in _sugar_icon_cache: + return _sugar_icon_cache[cache_key] + + logging.debug('sugar_icon: file_name=%s icon_name=%s width=%s height=%s ' \ + 'color=%s' % (file_name, icon_name, width, height, color)) + + is_svg = icon_filename.endswith('.svg') + + if is_svg: + handle = load_svg() + if width and height: + pixbuf = from_svg_at_size(handle=handle, width=width, height=height, + keep_ratio=True) + else: + pixbuf = handle.get_pixbuf() + else: + if width and height: + pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(icon_filename, + width, height) + else: + pixbuf = gtk.gdk.pixbuf_new_from_file(icon_filename) + + if insensitive_widget: + pixbuf = get_insensitive_pixbuf() - file_o = os.fdopen(file_d, 'w') - file_o.write(str) - file_o.close() + _sugar_icon_cache[cache_key] = pixbuf - out = gtk.gdk.pixbuf_new_from_file(path) - os.unlink(path) + return pixbuf - return out +_sugar_icon_cache = LRU(50) |