Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/mmm_modules/utils.py
blob: 3eeade064ca9ba00bd0e74d1780ed50a5ac3d752 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# Copyright 2007 World Wide Workshop Foundation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# If you find this activity useful or end up using parts of it in one of your
# own creations we would love to hear from you at info@WorldWideWorkshop.org !
#

from gi.repository import Gtk, GdkPixbuf
import logging

RESIZE_STRETCH = 1
RESIZE_CUT = 2
RESIZE_PAD = 3

TYPE_REG = []

def register_image_type (handler):
    TYPE_REG.append(handler)

def calculate_relative_size (orig_width, orig_height, width, height):
    """ If any of width or height is -1, the returned width or height will be in the same relative scale as the
    given part.
    >>> calculate_relative_size(100, 100, 50, -1)
    (50, 50)
    >>> calculate_relative_size(200, 100, -1, 50)
    (100, 50)

    If both width and height are given, the same values will be returned. If none is given, the orig_* will be returned.
    >>> calculate_relative_size(200,200,100,150)
    (100, 150)
    >>> calculate_relative_size(200,200,-1,-1)
    (200, 200)
    """
    if width < 0:
        if height >= 0:
            out_w = int(orig_width * (float(height)/orig_height))
            out_h = height
        else:
            out_w = orig_width
            out_h = orig_height
    else:
        out_w = width
        if height < 0:
            out_h = int(orig_height * (float(width)/orig_width))
        else:
            out_h = height
    return out_w, out_h

def load_image (filename, width=-1, height=-1, method=RESIZE_CUT):
    """ load an image from filename, returning it's gtk.gdk.PixBuf().
    If any or all of width and height are given, scale the loaded image to fit the given size(s).
    If both width and height and requested scaling can be achieved in two flavours, as defined by
    the method argument:
      RESIZE_CUT : resize so one of width or height fits the requirement and the other fits or overflows,
                   cut the center of the image to fit the request.
      RESIZE_STRETCH : fit the requested sizes exactly, by scaling with stretching sides if needed.
      RESIZE_PAD : resize so one of width or height fits the requirement and the other underflows.

    Example: Image with 500x500, requested 200x100
      - RESIZE_CUT: scale to 200x200, cut 50 off each top and bottom to fit 200x100
      - RESIZE STRETCH : scale to 200x100, by changing the image WxH ratio from 1:1 to 2:1, thus distorting it.
      - RESIZE_PAD: scale to 100x100, add 50 pixel padding for top and bottom to fit 200x100
    """
    for ht in TYPE_REG:
        if ht.can_handle(filename):
            return ht(width, height, filename)
#    if filename.lower().endswith('.sequence'):
#        slider = None
#        cmds = file(filename).readlines()
#        if len(cmds) > 1:
#            _x_ = eval(cmds[0])
#            items = []
#            for i in range(16):
#                items.append(_x_)
#                _x_ = eval(cmds[1])
#            slider = SliderCreator(width, height, items)
#            slider.prepare_stringed(2,2)
#        return slider
#
    img = Gtk.Image()
    try:
        img.set_from_file(filename)
        pb = img.get_pixbuf()
    except:
        return None
    return resize_image(pb, width, height, method)

def resize_image (pb, width=-1, height=-1, method=RESIZE_CUT):
    if pb is None:
        return None
    logging.debug("utils: method=%i" % method)
    if method == RESIZE_STRETCH or width == -1 or height == -1:
        w,h = calculate_relative_size(pb.get_width(), pb.get_height(), width, height)
        scaled_pb = pb.scale_simple(w,h, GdkPixbuf.InterpType.BILINEAR)
    elif method == RESIZE_PAD:
        w,h = pb.get_width(), pb.get_height()
        hr = float(height)/h
        wr = float(width)/w
        factor = min(hr, wr)
        w = w * factor
        h = h * factor
        logging.debug("RESIZE_PAD: %i,%i,%f" % (w,h,factor))
        scaled_pb = pb.scale_simple(int(w), int(h), GdkPixbuf.InterpType.BILINEAR)
    else: # RESIZE_CUT / default
        w,h = pb.get_width(), pb.get_height()
        if width > w:
            if height > h:
                #calc which side needs more scaling up as both are smaller
                hr = float(height)/h
                wr = float(width)/w
                if hr < wr:
                    w = width
                    h = -1
                else:
                    h = height
                    w = -1
            else:
                # requested height smaller than image, scale width up and cut on height
                h = -1
                w = width
        else:
            if height > h:
                #requested width smaller than image, scale height up and cut on width
                h = height
                w = -1
            else:
                # calc which side needs less scaling down as both are bigger
                hr = float(height)/h
                wr = float(width)/w
                if hr < wr:
                    w = width
                    h = -1
                else:
                    h = height
                    w = -1
        # w, h now have -1 for the side that should be relatively scaled, to keep the aspect ratio and
        # assuring that the image is at least as big as the request.
        w,h = calculate_relative_size(pb.get_width(), pb.get_height(), w,h)
        scaled_pb = pb.scale_simple(w,h, GdkPixbuf.InterpType.BILINEAR)
        # now we cut whatever is left to make the requested size
        scaled_pb = scaled_pb.subpixbuf(abs((width-w)/2),abs((height-h)/2), width, height)
    return scaled_pb

### Helper decorators

def trace (func):
    def wrapped (*args, **kwargs):
        logging.debug("TRACE %s %s %s" % (func.func_name, args, kwargs))
        return func(*args, **kwargs)
    return wrapped