1
"""Functions new to the pyScss library."""
3
from __future__ import absolute_import
12
from six.moves import xrange
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
20
from PIL import Image, ImageDraw
28
log = logging.getLogger(__name__)
30
EXTRA_LIBRARY = FunctionLibrary()
31
register = EXTRA_LIBRARY.register
34
# ------------------------------------------------------------------------------
36
def _image_noise(pixdata, size, density=None, intensity=None, color=None, opacity=None, monochrome=None, background=None):
39
elif not isinstance(density, (tuple, list)):
44
elif not isinstance(intensity, (tuple, list)):
45
intensity = [intensity]
48
color = [(0, 0, 0, 0)]
49
elif not isinstance(color, (tuple, list)) or not isinstance(color[0], (tuple, list)):
54
elif not isinstance(opacity, (tuple, list)):
59
elif not isinstance(monochrome, (tuple, list)):
60
monochrome = [monochrome]
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)
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
82
cx = 255 * (1 - _intensity)
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
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)
109
for pos, col in pixels.items():
112
pixdata[pos] = tuple(int(round(c)) for c in (col[0] / ca, col[1] / ca, col[2] / ca, ca * 255))
115
def _image_brushed(pixdata, size, density=None, intensity=None, color=None, opacity=None, monochrome=None, direction=None, spread=None, background=None):
118
elif not isinstance(density, (tuple, list)):
123
elif not isinstance(intensity, (tuple, list)):
124
intensity = [intensity]
127
color = [(0, 0, 0, 0)]
128
elif not isinstance(color, (tuple, list)) or not isinstance(color[0], (tuple, list)):
133
elif not isinstance(opacity, (tuple, list)):
138
elif not isinstance(monochrome, (tuple, list)):
139
monochrome = [monochrome]
143
elif not isinstance(direction, (tuple, list)):
144
direction = [direction]
148
elif not isinstance(spread, (tuple, list)):
156
pp = lambda x, y, o: ((x - o) % size, y)
158
pp = lambda x, y, o: ((x - o) % size, (y + x - o) % size)
160
pp = lambda x, y, o: (y, (x - o) % size)
162
pp = lambda x, y, o: ((x - o) % size, (y - x - o) % size)
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)
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
184
cx = 255 * _intensity
187
cx = 255 * (1 - _intensity)
188
cm = 255 * _intensity
200
pp = ppgen(_direction)
202
for y in xrange(size):
203
if _spread and (y + (l % 2)) % _spread:
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))
221
for x in xrange(size):
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)
258
for pos, col in pixels.items():
261
pixdata[pos] = tuple(int(round(c)) for c in (col[0] / ca, col[1] / ca, col[2] / ca, ca * 255))
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):
274
raise Exception("Images manipulation require PIL")
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)]
281
size = int(Number(size).value) if size else 0
282
if size < 1 or size > 512:
285
monochrome = bool(monochrome)
287
background = Color(background).value if background else None
289
new_image = Image.new(
294
pixdata = new_image.load()
295
_image_noise(pixdata, size, density, intensity, color, opacity, monochrome)
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('-', '_')
303
asset_path = os.path.join(config.ASSETS_ROOT or os.path.join(config.STATIC_ROOT, 'assets'), asset_file)
305
new_image.save(asset_path)
307
log.exception("Error while saving image")
308
inline = True # Retry inline version
309
url = '%s%s' % (config.ASSETS_URL, asset_file)
311
output = six.BytesIO()
312
new_image.save(output, format='PNG')
313
contents = output.getvalue()
315
url = 'data:image/png;base64,' + base64.b64encode(contents)
317
inline = 'url("%s")' % escape(url)
318
return String.unquoted(inline)
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):
333
raise Exception("Images manipulation require PIL")
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)]
340
size = int(Number(size).value) if size else -1
341
if size < 0 or size > 512:
344
monochrome = bool(monochrome)
346
direction = [Number(v).value for v in List.from_maybe(direction)]
347
spread = [Number(v).value for v in List.from_maybe(spread)]
349
background = Color(background).value if background else None
351
new_image = Image.new(
356
pixdata = new_image.load()
357
_image_brushed(pixdata, size, density, intensity, color, opacity, monochrome, direction, spread, background)
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('-', '_')
365
asset_path = os.path.join(config.ASSETS_ROOT or os.path.join(config.STATIC_ROOT, 'assets'), asset_file)
367
new_image.save(asset_path)
369
log.exception("Error while saving image")
370
inline = True # Retry inline version
371
url = '%s%s' % (config.ASSETS_URL, asset_file)
373
output = six.BytesIO()
374
new_image.save(output, format='PNG')
375
contents = output.getvalue()
377
url = 'data:image/png;base64,' + base64.b64encode(contents)
379
inline = 'url("%s")' % escape(url)
380
return String.unquoted(inline)
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):
387
raise Exception("Images manipulation require PIL")
388
if grid_color is None:
389
grid_color = (120, 170, 250, 15)
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)
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)
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:
409
_full_width = (_left_gutter + _width + _right_gutter)
410
new_image = Image.new(
412
size=(_full_width * int(columns), _height),
413
color=background_color
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)
419
draw.rectangle((0, _height - 1, _full_width * int(columns) - 1, _height - 1), fill=baseline_color)
423
grid_name += str(int(left_gutter)) + '+'
424
grid_name += str(int(width))
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)
434
new_image.save(asset_path)
436
log.exception("Error while saving image")
437
inline = True # Retry inline version
438
url = '%s%s' % (config.ASSETS_URL, asset_file)
440
output = six.BytesIO()
441
new_image.save(output, format='PNG')
442
contents = output.getvalue()
444
url = 'data:image/png;base64,' + base64.b64encode(contents)
445
inline = 'url("%s")' % escape(url)
446
return String.unquoted(inline)
449
@register('image-color', 1)
450
@register('image-color', 2)
451
@register('image-color', 3)
452
def image_color(color, width=1, height=1):
454
raise Exception("Images manipulation require PIL")
455
w = int(Number(width).value)
456
h = int(Number(height).value)
459
new_image = Image.new(
460
mode='RGB' if color.alpha == 1 else 'RGBA',
464
output = six.BytesIO()
465
new_image.save(output, format='PNG')
466
contents = output.getvalue()
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)