~ubuntu-branches/ubuntu/natty/miro/natty

« back to all changes in this revision

Viewing changes to lib/frontends/widgets/imagepool.py

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2011-01-22 02:46:33 UTC
  • mfrom: (1.4.10 upstream) (1.7.5 experimental)
  • Revision ID: james.westby@ubuntu.com-20110122024633-kjme8u93y2il5nmf
Tags: 3.5.1-1ubuntu1
* Merge from debian.  Remaining ubuntu changes:
  - Use python 2.7 instead of python 2.6
  - Relax dependency on python-dbus to >= 0.83.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Miro - an RSS based video player application
 
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
17
#
 
18
# In addition, as a special exception, the copyright holders give
 
19
# permission to link the code of portions of this program with the OpenSSL
 
20
# library.
 
21
#
 
22
# You must obey the GNU General Public License in all respects for all of
 
23
# the code used other than OpenSSL. If you modify file(s) with this
 
24
# exception, you may extend this exception to your version of the file(s),
 
25
# but you are not obligated to do so. If you do not wish to do so, delete
 
26
# this exception statement from your version. If you delete this exception
 
27
# statement from all source files in the program, then also delete it here.
 
28
 
 
29
"""``miro.frontends.widgets.imagepool`` -- Get Image objects for image
 
30
filenames.
 
31
 
 
32
imagepool handles creating Image and ImageSurface objects for image
 
33
filenames.  It remembers images that have been created, and doesn't
 
34
create duplicate Image/ImageSurface objects for a single path.
 
35
"""
 
36
 
 
37
import logging
 
38
import traceback
 
39
import weakref
 
40
 
 
41
from miro.plat import resources
 
42
from miro.plat.frontends.widgets import widgetset
 
43
 
 
44
# path_to_image and path_to_surface maps (path, size) tuples to
 
45
# Image/ImageSurface objects.
 
46
# Uses weak references so that once the Image/ImageSurface is not being used
 
47
# it will be deleted.
 
48
path_to_image = weakref.WeakValueDictionary()
 
49
path_to_surface = weakref.WeakValueDictionary()
 
50
 
 
51
broken_image = widgetset.Image(resources.path('images/broken-image.gif'))
 
52
 
 
53
def scaled_size(image, size):
 
54
    """Takes an image which has a width and a height and a size tuple
 
55
    that specifies the space available and returns the new width
 
56
    and height that allows the image to fit into the sized space
 
57
    at the correct height/width ratio.
 
58
 
 
59
    :param image: the Image (must have width and height properties)
 
60
    :param size: (width, height) tuple of space you want the image
 
61
                 to fit in
 
62
    """
 
63
    if image.width == 0 or image.height == 0:
 
64
        image = broken_image
 
65
    image_ratio = float(image.width) / image.height
 
66
    new_ratio = float(size[0]) / size[1]
 
67
    if image_ratio == new_ratio:
 
68
        return size
 
69
    elif image_ratio > new_ratio:
 
70
        # The scaled image has a wider aspect ratio than the old one.
 
71
        height = int(round(float(size[0]) / image.width * image.height))
 
72
        return size[0], height
 
73
    else:
 
74
        # The scaled image has a taller aspect ratio than the old one.
 
75
        width = int(round(float(size[1]) / image.height * image.width))
 
76
        return width, size[1]
 
77
 
 
78
def get(path, size=None):
 
79
    """Returns an Image for path.
 
80
 
 
81
    :param path: the filename for the image
 
82
    :param size: if the image needs to fit into a specified sized
 
83
                 space, then specify this and get will return a
 
84
                 scaled image; if size is not specified, then this
 
85
                 returns the default sized image
 
86
    """
 
87
    try:
 
88
        return path_to_image[(path, size)]
 
89
    except KeyError:
 
90
        try:
 
91
            image = widgetset.Image(path)
 
92
        except StandardError:
 
93
            logging.warn("error loading image %s:\n%s", path,
 
94
                    traceback.format_exc())
 
95
            image = broken_image
 
96
        if size is not None:
 
97
            image = image.resize(*scaled_size(image, size))
 
98
            path_to_image[(path, size)] = image
 
99
        else:
 
100
            path_to_image[(path, None)] = image
 
101
            path_to_image[(path, (image.width, image.height))] = image
 
102
        return image
 
103
 
 
104
def get_surface(path, size=None):
 
105
    """Returns an ImageSurface for path.
 
106
 
 
107
    :param path: the filename for the image
 
108
    :param size: if the image needs to fit into a specified sized
 
109
                 space, then specify this and get will return a
 
110
                 scaled image; if size is not specified, then this
 
111
                 returns the default sized image
 
112
    """
 
113
    try:
 
114
        return path_to_surface[(path, size)]
 
115
    except KeyError:
 
116
        image = get(path, size)
 
117
        surface = widgetset.ImageSurface(image)
 
118
        path_to_surface[(path, size)] = surface
 
119
        if size is None:
 
120
            path_to_surface[(path, (image.width, image.height))] = surface
 
121
        return surface
 
122
 
 
123
class LazySurface(object):
 
124
    """Lazily loaded ImageSurface.  
 
125
    
 
126
    LazySurface objects only create ImageSurfaces as needed.  If multiple
 
127
    LazySurface objects are created for the same path, then they will share
 
128
    the underlying ImageSurface object.
 
129
    """
 
130
    def __init__(self, path, size=None):
 
131
        self.path = path
 
132
        self.size = size
 
133
        self._get_surface_if_available()
 
134
 
 
135
    def _get_surface_if_available(self):
 
136
        """Try to get the ImageSurface for this object if it's already
 
137
        created.  This ensures that if the other ImageSurface is destroyed, we
 
138
        will still have a reference.
 
139
        """
 
140
        try:
 
141
            self._surface = path_to_surface[(self.path, self.size)]
 
142
        except KeyError:
 
143
            pass
 
144
 
 
145
    def _ensure_surface(self):
 
146
        if not hasattr(self, '_surface'):
 
147
            self._surface = get_surface(self.path, self.size)
 
148
 
 
149
    def get_width(self):
 
150
        self._ensure_surface()
 
151
        return self._surface.width
 
152
    width = property(get_width)
 
153
 
 
154
    def get_height(self):
 
155
        self._ensure_surface()
 
156
        return self._surface.height
 
157
    height = property(get_height)
 
158
 
 
159
    def draw(self, context, x, y, width, height, fraction=1.0):
 
160
        self._ensure_surface()
 
161
        self._surface.draw(context, x, y, width, height, fraction)