~corey.bryant/ubuntu/wily/python-pyscss/thedac

« back to all changes in this revision

Viewing changes to scss/functions/extra.py

  • Committer: Package Import Robot
  • Author(s): Thomas Goirand
  • Date: 2014-06-26 12:10:36 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20140626121036-3dv13zn5zptk9fpx
Tags: 1.2.0.post3-1
* Team upload.
* New upstream release (Closes: #738776).
* Added a debian/gbp.conf.
* Added missing ${python:Depends}
* Added Python 3 support.
* Removed duplicate debhelper build-depends.
* Cannonical VCS URLs.
* Standards-Version: is now 3.9.5.
* Added a watch file.
* override dh helpers which the package doesn't use.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Functions new to the pyScss library."""
 
2
 
 
3
from __future__ import absolute_import
 
4
 
 
5
import base64
 
6
import hashlib
 
7
import logging
 
8
import os.path
 
9
import random
 
10
 
 
11
import six
 
12
from six.moves import xrange
 
13
 
 
14
from scss import config
 
15
from scss.functions.library import FunctionLibrary
 
16
from scss.types import Color, Number, String, List
 
17
from scss.util import escape
 
18
 
 
19
try:
 
20
    from PIL import Image, ImageDraw
 
21
except ImportError:
 
22
    try:
 
23
        import Image
 
24
        import ImageDraw
 
25
    except:
 
26
        Image = None
 
27
 
 
28
log = logging.getLogger(__name__)
 
29
 
 
30
EXTRA_LIBRARY = FunctionLibrary()
 
31
register = EXTRA_LIBRARY.register
 
32
 
 
33
 
 
34
# ------------------------------------------------------------------------------
 
35
# Image stuff
 
36
def _image_noise(pixdata, size, density=None, intensity=None, color=None, opacity=None, monochrome=None, background=None):
 
37
    if not density:
 
38
        density = [0.8]
 
39
    elif not isinstance(density, (tuple, list)):
 
40
        density = [density]
 
41
 
 
42
    if not intensity:
 
43
        intensity = [0.5]
 
44
    elif not isinstance(intensity, (tuple, list)):
 
45
        intensity = [intensity]
 
46
 
 
47
    if not color:
 
48
        color = [(0, 0, 0, 0)]
 
49
    elif not isinstance(color, (tuple, list)) or not isinstance(color[0], (tuple, list)):
 
50
        color = [color]
 
51
 
 
52
    if not opacity:
 
53
        opacity = [0.2]
 
54
    elif not isinstance(opacity, (tuple, list)):
 
55
        opacity = [opacity]
 
56
 
 
57
    if not monochrome:
 
58
        monochrome = [False]
 
59
    elif not isinstance(monochrome, (tuple, list)):
 
60
        monochrome = [monochrome]
 
61
 
 
62
    pixels = {}
 
63
 
 
64
    if background:
 
65
        for y in xrange(size):
 
66
            for x in xrange(size):
 
67
                ca = float(background[3])
 
68
                pixels[(x, y)] = (background[0] * ca, background[1] * ca, background[2] * ca, ca)
 
69
 
 
70
    loops = max(map(len, (density, intensity, color, opacity, monochrome)))
 
71
    for l in range(loops):
 
72
        _density = density[l % len(density)]
 
73
        _intensity = intensity[l % len(intensity)]
 
74
        _color = color[l % len(color)]
 
75
        _opacity = opacity[l % len(opacity)]
 
76
        _monochrome = monochrome[l % len(monochrome)]
 
77
        _intensity = 1 - _intensity
 
78
        if _intensity < 0.5:
 
79
            cx = 255 * _intensity
 
80
            cm = cx
 
81
        else:
 
82
            cx = 255 * (1 - _intensity)
 
83
            cm = 255 * _intensity
 
84
        xa = int(cm - cx)
 
85
        xb = int(cm + cx)
 
86
        if xa > 0:
 
87
            xa &= 255
 
88
        else:
 
89
            xa = 0
 
90
        if xb > 0:
 
91
            xb &= 255
 
92
        else:
 
93
            xb = 0
 
94
        r, g, b, a = _color
 
95
        for i in xrange(int(round(_density * size ** 2))):
 
96
            x = random.randint(1, size)
 
97
            y = random.randint(1, size)
 
98
            cc = random.randint(xa, xb)
 
99
            cr = (cc) * (1 - a) + a * r
 
100
            cg = (cc if _monochrome else random.randint(xa, xb)) * (1 - a) + a * g
 
101
            cb = (cc if _monochrome else random.randint(xa, xb)) * (1 - a) + a * b
 
102
            ca = random.random() * _opacity
 
103
            ica = 1 - ca
 
104
            pos = (x - 1, y - 1)
 
105
            dst = pixels.get(pos, (0, 0, 0, 0))
 
106
            src = (cr * ca, cg * ca, cb * ca, ca)
 
107
            pixels[pos] = (src[0] + dst[0] * ica, src[1] + dst[1] * ica, src[2] + dst[2] * ica, src[3] + dst[3] * ica)
 
108
 
 
109
    for pos, col in pixels.items():
 
110
        ca = col[3]
 
111
        if ca:
 
112
            pixdata[pos] = tuple(int(round(c)) for c in (col[0] / ca, col[1] / ca, col[2] / ca, ca * 255))
 
113
 
 
114
 
 
115
def _image_brushed(pixdata, size, density=None, intensity=None, color=None, opacity=None, monochrome=None, direction=None, spread=None, background=None):
 
116
    if not density:
 
117
        density = [0.8]
 
118
    elif not isinstance(density, (tuple, list)):
 
119
        density = [density]
 
120
 
 
121
    if not intensity:
 
122
        intensity = [0.5]
 
123
    elif not isinstance(intensity, (tuple, list)):
 
124
        intensity = [intensity]
 
125
 
 
126
    if not color:
 
127
        color = [(0, 0, 0, 0)]
 
128
    elif not isinstance(color, (tuple, list)) or not isinstance(color[0], (tuple, list)):
 
129
        color = [color]
 
130
 
 
131
    if not opacity:
 
132
        opacity = [0.2]
 
133
    elif not isinstance(opacity, (tuple, list)):
 
134
        opacity = [opacity]
 
135
 
 
136
    if not monochrome:
 
137
        monochrome = [False]
 
138
    elif not isinstance(monochrome, (tuple, list)):
 
139
        monochrome = [monochrome]
 
140
 
 
141
    if not direction:
 
142
        direction = [0]
 
143
    elif not isinstance(direction, (tuple, list)):
 
144
        direction = [direction]
 
145
 
 
146
    if not spread:
 
147
        spread = [0]
 
148
    elif not isinstance(spread, (tuple, list)):
 
149
        spread = [spread]
 
150
 
 
151
    def ppgen(d):
 
152
        if d is None:
 
153
            return
 
154
        d = d % 4
 
155
        if d == 0:
 
156
            pp = lambda x, y, o: ((x - o) % size, y)
 
157
        elif d == 1:
 
158
            pp = lambda x, y, o: ((x - o) % size, (y + x - o) % size)
 
159
        elif d == 2:
 
160
            pp = lambda x, y, o: (y, (x - o) % size)
 
161
        else:
 
162
            pp = lambda x, y, o: ((x - o) % size, (y - x - o) % size)
 
163
        return pp
 
164
 
 
165
    pixels = {}
 
166
 
 
167
    if background:
 
168
        for y in xrange(size):
 
169
            for x in xrange(size):
 
170
                ca = float(background[3])
 
171
                pixels[(x, y)] = (background[0] * ca, background[1] * ca, background[2] * ca, ca)
 
172
 
 
173
    loops = max(map(len, (density, intensity, color, opacity, monochrome, direction, spread)))
 
174
    for l in range(loops):
 
175
        _density = density[l % len(density)]
 
176
        _intensity = intensity[l % len(intensity)]
 
177
        _color = color[l % len(color)]
 
178
        _opacity = opacity[l % len(opacity)]
 
179
        _monochrome = monochrome[l % len(monochrome)]
 
180
        _direction = direction[l % len(direction)]
 
181
        _spread = spread[l % len(spread)]
 
182
        _intensity = 1 - _intensity
 
183
        if _intensity < 0.5:
 
184
            cx = 255 * _intensity
 
185
            cm = cx
 
186
        else:
 
187
            cx = 255 * (1 - _intensity)
 
188
            cm = 255 * _intensity
 
189
        xa = int(cm - cx)
 
190
        xb = int(cm + cx)
 
191
        if xa > 0:
 
192
            xa &= 255
 
193
        else:
 
194
            xa = 0
 
195
        if xb > 0:
 
196
            xb &= 255
 
197
        else:
 
198
            xb = 0
 
199
        r, g, b, a = _color
 
200
        pp = ppgen(_direction)
 
201
        if pp:
 
202
            for y in xrange(size):
 
203
                if _spread and (y + (l % 2)) % _spread:
 
204
                    continue
 
205
                o = random.randint(1, size)
 
206
                cc = random.randint(xa, xb)
 
207
                cr = (cc) * (1 - a) + a * r
 
208
                cg = (cc if _monochrome else random.randint(xa, xb)) * (1 - a) + a * g
 
209
                cb = (cc if _monochrome else random.randint(xa, xb)) * (1 - a) + a * b
 
210
                da = random.randint(0, 255) * _opacity
 
211
                ip = round((size / 2.0 * _density) / int(1 / _density))
 
212
                iq = round((size / 2.0 * (1 - _density)) / int(1 / _density))
 
213
                if ip:
 
214
                    i = da / ip
 
215
                    aa = 0
 
216
                else:
 
217
                    i = 0
 
218
                    aa = da
 
219
                d = 0
 
220
                p = ip
 
221
                for x in xrange(size):
 
222
                    if d == 0:
 
223
                        if p > 0:
 
224
                            p -= 1
 
225
                            aa += i
 
226
                        else:
 
227
                            d = 1
 
228
                            q = iq
 
229
                    elif d == 1:
 
230
                        if q > 0:
 
231
                            q -= 1
 
232
                        else:
 
233
                            d = 2
 
234
                            p = ip
 
235
                    elif d == 2:
 
236
                        if p > 0:
 
237
                            p -= 1
 
238
                            aa -= i
 
239
                        else:
 
240
                            d = 3
 
241
                            q = iq
 
242
                    elif d == 3:
 
243
                        if q > 0:
 
244
                            q -= 1
 
245
                        else:
 
246
                            d = 0
 
247
                            p = ip
 
248
                    if aa > 0:
 
249
                        ca = aa / 255.0
 
250
                    else:
 
251
                        ca = 0.0
 
252
                    ica = 1 - ca
 
253
                    pos = pp(x, y, o)
 
254
                    dst = pixels.get(pos, (0, 0, 0, 0))
 
255
                    src = (cr * ca, cg * ca, cb * ca, ca)
 
256
                    pixels[pos] = (src[0] + dst[0] * ica, src[1] + dst[1] * ica, src[2] + dst[2] * ica, src[3] + dst[3] * ica)
 
257
 
 
258
    for pos, col in pixels.items():
 
259
        ca = col[3]
 
260
        if ca:
 
261
            pixdata[pos] = tuple(int(round(c)) for c in (col[0] / ca, col[1] / ca, col[2] / ca, ca * 255))
 
262
 
 
263
 
 
264
@register('background-noise', 0)
 
265
@register('background-noise', 1)
 
266
@register('background-noise', 2)
 
267
@register('background-noise', 3)
 
268
@register('background-noise', 4)
 
269
@register('background-noise', 5)
 
270
@register('background-noise', 6)
 
271
@register('background-noise', 7)
 
272
def background_noise(density=None, opacity=None, size=None, monochrome=False, intensity=(), color=None, background=None, inline=False):
 
273
    if not Image:
 
274
        raise Exception("Images manipulation require PIL")
 
275
 
 
276
    density = [Number(v).value for v in List.from_maybe(density)]
 
277
    intensity = [Number(v).value for v in List.from_maybe(intensity)]
 
278
    color = [Color(v).value for v in List.from_maybe(color) if v]
 
279
    opacity = [Number(v).value for v in List.from_maybe(opacity)]
 
280
 
 
281
    size = int(Number(size).value) if size else 0
 
282
    if size < 1 or size > 512:
 
283
        size = 200
 
284
 
 
285
    monochrome = bool(monochrome)
 
286
 
 
287
    background = Color(background).value if background else None
 
288
 
 
289
    new_image = Image.new(
 
290
        mode='RGBA',
 
291
        size=(size, size)
 
292
    )
 
293
 
 
294
    pixdata = new_image.load()
 
295
    _image_noise(pixdata, size, density, intensity, color, opacity, monochrome)
 
296
 
 
297
    if not inline:
 
298
        key = (size, density, intensity, color, opacity, monochrome)
 
299
        asset_file = 'noise-%s%sx%s' % ('mono-' if monochrome else '', size, size)
 
300
        # asset_file += '-[%s][%s]' % ('-'.join(to_str(s).replace('.', '_') for s in density or []), '-'.join(to_str(s).replace('.', '_') for s in opacity or []))
 
301
        asset_file += '-' + base64.urlsafe_b64encode(hashlib.md5(repr(key)).digest()).rstrip('=').replace('-', '_')
 
302
        asset_file += '.png'
 
303
        asset_path = os.path.join(config.ASSETS_ROOT or os.path.join(config.STATIC_ROOT, 'assets'), asset_file)
 
304
        try:
 
305
            new_image.save(asset_path)
 
306
        except IOError:
 
307
            log.exception("Error while saving image")
 
308
            inline = True  # Retry inline version
 
309
        url = '%s%s' % (config.ASSETS_URL, asset_file)
 
310
    if inline:
 
311
        output = six.BytesIO()
 
312
        new_image.save(output, format='PNG')
 
313
        contents = output.getvalue()
 
314
        output.close()
 
315
        url = 'data:image/png;base64,' + base64.b64encode(contents)
 
316
 
 
317
    inline = 'url("%s")' % escape(url)
 
318
    return String.unquoted(inline)
 
319
 
 
320
 
 
321
@register('background-brushed', 0)
 
322
@register('background-brushed', 1)
 
323
@register('background-brushed', 2)
 
324
@register('background-brushed', 3)
 
325
@register('background-brushed', 4)
 
326
@register('background-brushed', 5)
 
327
@register('background-brushed', 6)
 
328
@register('background-brushed', 7)
 
329
@register('background-brushed', 8)
 
330
@register('background-brushed', 9)
 
331
def background_brushed(density=None, intensity=None, color=None, opacity=None, size=None, monochrome=False, direction=(), spread=(), background=None, inline=False):
 
332
    if not Image:
 
333
        raise Exception("Images manipulation require PIL")
 
334
 
 
335
    density = [Number(v).value for v in List.from_maybe(density)]
 
336
    intensity = [Number(v).value for v in List.from_maybe(intensity)]
 
337
    color = [Color(v).value for v in List.from_maybe(color) if v]
 
338
    opacity = [Number(v).value for v in List.from_maybe(opacity)]
 
339
 
 
340
    size = int(Number(size).value) if size else -1
 
341
    if size < 0 or size > 512:
 
342
        size = 200
 
343
 
 
344
    monochrome = bool(monochrome)
 
345
 
 
346
    direction = [Number(v).value for v in List.from_maybe(direction)]
 
347
    spread = [Number(v).value for v in List.from_maybe(spread)]
 
348
 
 
349
    background = Color(background).value if background else None
 
350
 
 
351
    new_image = Image.new(
 
352
        mode='RGBA',
 
353
        size=(size, size)
 
354
    )
 
355
 
 
356
    pixdata = new_image.load()
 
357
    _image_brushed(pixdata, size, density, intensity, color, opacity, monochrome, direction, spread, background)
 
358
 
 
359
    if not inline:
 
360
        key = (size, density, intensity, color, opacity, monochrome, direction, spread, background)
 
361
        asset_file = 'brushed-%s%sx%s' % ('mono-' if monochrome else '', size, size)
 
362
        # asset_file += '-[%s][%s][%s]' % ('-'.join(to_str(s).replace('.', '_') for s in density or []), '-'.join(to_str(s).replace('.', '_') for s in opacity or []), '-'.join(to_str(s).replace('.', '_') for s in direction or []))
 
363
        asset_file += '-' + base64.urlsafe_b64encode(hashlib.md5(repr(key)).digest()).rstrip('=').replace('-', '_')
 
364
        asset_file += '.png'
 
365
        asset_path = os.path.join(config.ASSETS_ROOT or os.path.join(config.STATIC_ROOT, 'assets'), asset_file)
 
366
        try:
 
367
            new_image.save(asset_path)
 
368
        except IOError:
 
369
            log.exception("Error while saving image")
 
370
            inline = True  # Retry inline version
 
371
        url = '%s%s' % (config.ASSETS_URL, asset_file)
 
372
    if inline:
 
373
        output = six.BytesIO()
 
374
        new_image.save(output, format='PNG')
 
375
        contents = output.getvalue()
 
376
        output.close()
 
377
        url = 'data:image/png;base64,' + base64.b64encode(contents)
 
378
 
 
379
    inline = 'url("%s")' % escape(url)
 
380
    return String.unquoted(inline)
 
381
 
 
382
 
 
383
@register('grid-image', 4)
 
384
@register('grid-image', 5)
 
385
def _grid_image(left_gutter, width, right_gutter, height, columns=1, grid_color=None, baseline_color=None, background_color=None, inline=False):
 
386
    if not Image:
 
387
        raise Exception("Images manipulation require PIL")
 
388
    if grid_color is None:
 
389
        grid_color = (120, 170, 250, 15)
 
390
    else:
 
391
        c = Color(grid_color).value
 
392
        grid_color = (c[0], c[1], c[2], int(c[3] * 255.0))
 
393
    if baseline_color is None:
 
394
        baseline_color = (120, 170, 250, 30)
 
395
    else:
 
396
        c = Color(baseline_color).value
 
397
        baseline_color = (c[0], c[1], c[2], int(c[3] * 255.0))
 
398
    if background_color is None:
 
399
        background_color = (0, 0, 0, 0)
 
400
    else:
 
401
        c = Color(background_color).value
 
402
        background_color = (c[0], c[1], c[2], int(c[3] * 255.0))
 
403
    _height = int(height) if height >= 1 else int(height * 1000.0)
 
404
    _width = int(width) if width >= 1 else int(width * 1000.0)
 
405
    _left_gutter = int(left_gutter) if left_gutter >= 1 else int(left_gutter * 1000.0)
 
406
    _right_gutter = int(right_gutter) if right_gutter >= 1 else int(right_gutter * 1000.0)
 
407
    if _height <= 0 or _width <= 0 or _left_gutter <= 0 or _right_gutter <= 0:
 
408
        raise ValueError
 
409
    _full_width = (_left_gutter + _width + _right_gutter)
 
410
    new_image = Image.new(
 
411
        mode='RGBA',
 
412
        size=(_full_width * int(columns), _height),
 
413
        color=background_color
 
414
    )
 
415
    draw = ImageDraw.Draw(new_image)
 
416
    for i in range(int(columns)):
 
417
        draw.rectangle((i * _full_width + _left_gutter, 0, i * _full_width + _left_gutter + _width - 1, _height - 1),  fill=grid_color)
 
418
    if _height > 1:
 
419
        draw.rectangle((0, _height - 1, _full_width * int(columns) - 1, _height - 1),  fill=baseline_color)
 
420
    if not inline:
 
421
        grid_name = 'grid_'
 
422
        if left_gutter:
 
423
            grid_name += str(int(left_gutter)) + '+'
 
424
        grid_name += str(int(width))
 
425
        if right_gutter:
 
426
            grid_name += '+' + str(int(right_gutter))
 
427
        if height and height > 1:
 
428
            grid_name += 'x' + str(int(height))
 
429
        key = (columns, grid_color, baseline_color, background_color)
 
430
        key = grid_name + '-' + base64.urlsafe_b64encode(hashlib.md5(repr(key)).digest()).rstrip('=').replace('-', '_')
 
431
        asset_file = key + '.png'
 
432
        asset_path = os.path.join(config.ASSETS_ROOT or os.path.join(config.STATIC_ROOT, 'assets'), asset_file)
 
433
        try:
 
434
            new_image.save(asset_path)
 
435
        except IOError:
 
436
            log.exception("Error while saving image")
 
437
            inline = True  # Retry inline version
 
438
        url = '%s%s' % (config.ASSETS_URL, asset_file)
 
439
    if inline:
 
440
        output = six.BytesIO()
 
441
        new_image.save(output, format='PNG')
 
442
        contents = output.getvalue()
 
443
        output.close()
 
444
        url = 'data:image/png;base64,' + base64.b64encode(contents)
 
445
    inline = 'url("%s")' % escape(url)
 
446
    return String.unquoted(inline)
 
447
 
 
448
 
 
449
@register('image-color', 1)
 
450
@register('image-color', 2)
 
451
@register('image-color', 3)
 
452
def image_color(color, width=1, height=1):
 
453
    if not Image:
 
454
        raise Exception("Images manipulation require PIL")
 
455
    w = int(Number(width).value)
 
456
    h = int(Number(height).value)
 
457
    if w <= 0 or h <= 0:
 
458
        raise ValueError
 
459
    new_image = Image.new(
 
460
        mode='RGB' if color.alpha == 1 else 'RGBA',
 
461
        size=(w, h),
 
462
        color=color.rgba255,
 
463
    )
 
464
    output = six.BytesIO()
 
465
    new_image.save(output, format='PNG')
 
466
    contents = output.getvalue()
 
467
    output.close()
 
468
    mime_type = 'image/png'
 
469
    url = 'data:' + mime_type + ';base64,' + base64.b64encode(contents)
 
470
    inline = 'url("%s")' % escape(url)
 
471
    return String.unquoted(inline)