/
Sandwich.py
131 lines (97 loc) · 4.5 KB
/
Sandwich.py
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
from . import Core
import Blit
blend_modes = {
'screen': Blit.blends.screen,
'add': Blit.blends.add,
'multiply': Blit.blends.multiply,
'subtract': Blit.blends.subtract,
'linear light': Blit.blends.linear_light,
'hard light': Blit.blends.hard_light
}
adjustment_names = {
'threshold': Blit.adjustments.threshold,
'curves': Blit.adjustments.curves,
'curves2': Blit.adjustments.curves2
}
class Provider:
"""
"""
def __init__(self, layer, stack):
self.config = layer.config
self.stack = stack
def renderTile(self, width, height, srs, coord):
# start with an empty base
rendered = Blit.Color(0, 0, 0, 0x10)
# a place to put rendered tiles
tiles = dict()
for layer in self.stack:
#
# Prepare pixels from elsewhere.
#
source_name, mask_name, color_name = [layer.get(k, None) for k in ('src', 'mask', 'color')]
if source_name and color_name and mask_name:
raise Core.KnownUnknown("You can't specify src, color and mask together in a Sandwich Layer: %s, %s, %s" % (repr(source_name), repr(color_name), repr(mask_name)))
if source_name and source_name not in tiles:
provider = self.config.layers[source_name].provider
tiles[source_name] = Blit.Bitmap(provider.renderTile(width, height, srs, coord))
if mask_name and mask_name not in tiles:
provider = self.config.layers[mask_name].provider
tiles[mask_name] = Blit.Bitmap(provider.renderTile(width, height, srs, coord))
#
# Build up the foreground layer.
#
if source_name and color_name:
# color first, then layer
foreground = make_color(color_name).blend(tiles[source_name])
elif source_name:
foreground = tiles[source_name]
elif color_name:
foreground = make_color(color_name)
elif mask_name:
raise Core.KnownUnknown("You have to provide more than just a mask to Sandwich Layer: %s" % repr(mask_name))
else:
raise Core.KnownUnknown("You have to provide at least some combination of src, color and mask to Sandwich Layer")
#
# Do the final composition with adjustments and blend modes.
#
for (name, args) in layer.get('adjustments', []):
adjustfunc = adjustment_names.get(name)(*args)
foreground = foreground.adjust(adjustfunc)
opacity = float(layer.get('opacity', 1.0))
blendfunc = blend_modes.get(layer.get('mode', None), None)
if mask_name:
rendered = rendered.blend(foreground, tiles[mask_name], opacity, blendfunc)
else:
rendered = rendered.blend(foreground, None, opacity, blendfunc)
#
if rendered.size() == (width, height):
return rendered.image()
else:
return rendered.image().resize((width, height))
def make_color(color):
""" Convert colors expressed as HTML-style RGB(A) strings to Blit.Color.
Examples:
white: "#ffffff", "#fff", "#ffff", "#ffffffff"
black: "#000000", "#000", "#000f", "#000000ff"
null: "#0000", "#00000000"
orange: "#f90", "#ff9900", "#ff9900ff"
transparent orange: "#f908", "#ff990088"
"""
if type(color) not in (str, unicode):
raise Core.KnownUnknown('Color must be a string: %s' % repr(color))
if color[0] != '#':
raise Core.KnownUnknown('Color must start with hash: "%s"' % color)
if len(color) not in (4, 5, 7, 9):
raise Core.KnownUnknown('Color must have three, four, six or seven hex chars: "%s"' % color)
if len(color) == 4:
color = ''.join([color[i] for i in (0, 1, 1, 2, 2, 3, 3)])
elif len(color) == 5:
color = ''.join([color[i] for i in (0, 1, 1, 2, 2, 3, 3, 4, 4)])
try:
r = int(color[1:3], 16)
g = int(color[3:5], 16)
b = int(color[5:7], 16)
a = len(color) == 7 and 0xFF or int(color[7:9], 16)
except ValueError:
raise Core.KnownUnknown('Color must be made up of valid hex chars: "%s"' % color)
return Blit.Color(r, g, b, a)