/
layer_effects.py
247 lines (193 loc) · 8.12 KB
/
layer_effects.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
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals, print_function
import warnings
import io
from psd_tools.decoder import decoders
from psd_tools.decoder.actions import decode_descriptor, UnknownOSType
from psd_tools.decoder.color import decode_color
from psd_tools.exceptions import Error
from psd_tools.utils import read_fmt
from psd_tools.constants import EffectOSType, BlendMode
from psd_tools.debug import pretty_namedtuple
_effect_info_decoders, register = decoders.new_registry()
Effects = pretty_namedtuple('Effects', 'version effects_count effects_list')
_LayerEffect = pretty_namedtuple('LayerEffect', 'effect_type effect_info')
ObjectBasedEffects = pretty_namedtuple('ObjectBasedEffects', 'version descriptor_version descriptor')
CommonStateInfo = pretty_namedtuple('CommonStateInfo', 'version visible unused')
ShadowInfo = pretty_namedtuple('ShadowInfo', 'version enabled '
'blend_mode color opacity '
'angle use_global_angle '
'distance intensity blur '
'native_color')
OuterGlowInfo = pretty_namedtuple('OuterGlowInfo', 'version enabled '
'blend_mode opacity color '
'intensity blur '
'native_color')
InnerGlowInfo = pretty_namedtuple('InnerGlowInfo', 'version enabled '
'blend_mode opacity color '
'intensity blur '
'invert native_color')
BevelInfo = pretty_namedtuple('BevelInfo', 'version enabled '
'bevel_style '
'depth direction blur '
'angle use_global_angle '
'highlight_blend_mode highlight_color highlight_opacity '
'shadow_blend_mode shadow_color shadow_opacity '
'real_highlight_color real_shadow_color')
SolidFillInfo = pretty_namedtuple('SolidFillInfo', 'version enabled '
'blend_mode color opacity '
'native_color')
class LayerEffect(_LayerEffect):
def __repr__(self):
return "LayerEffect(%s %s, %s)" % (self.effect_type, EffectOSType.name_of(self.effect_type),
self.effect_info)
def _repr_pretty_(self, p, cycle):
# IS NOT TESTED!!
if cycle:
p.text('LayerEffect(...)')
else:
with p.group(1, 'LayerEffect(', ')'):
p.breakable()
p.text("%s %s," % (self.effect_type, EffectOSType.name_of(self.effect_type)))
p.breakable()
p.pretty(self.effect_info)
def decode(effects):
"""
Reads and decodes info about layer effects.
"""
fp = io.BytesIO(effects)
version, effects_count = read_fmt("HH", fp)
effects_list = []
for idx in range(effects_count):
sig = fp.read(4)
if sig != b'8BIM':
raise Error("Error parsing layer effect: invalid signature (%r)" % sig)
effect_type = fp.read(4)
if not EffectOSType.is_known(effect_type):
warnings.warn("Unknown effect type (%s)" % effect_type)
effect_info_length = read_fmt("I", fp)[0]
effect_info = fp.read(effect_info_length)
decoder = _effect_info_decoders.get(effect_type, lambda data: data)
effects_list.append(LayerEffect(effect_type, decoder(effect_info)))
return Effects(version, effects_count, effects_list)
def decode_object_based(effects):
"""
Reads and decodes info about object-based layer effects.
"""
fp = io.BytesIO(effects)
version, descriptor_version = read_fmt("II", fp)
try:
descriptor = decode_descriptor(None, fp)
except UnknownOSType as e:
warnings.warn("Ignoring object-based layer effects tagged block (%s)" % e)
return effects
return ObjectBasedEffects(version, descriptor_version, descriptor)
def _read_blend_mode(fp):
sig = fp.read(4)
if sig != b'8BIM':
raise Error("Error parsing layer effect: invalid signature (%r)" % sig)
blend_mode = fp.read(4)
if not BlendMode.is_known(blend_mode):
warnings.warn("Unknown blend mode (%s)" % blend_mode)
return blend_mode
@register(EffectOSType.COMMON_STATE)
def _decode_common_info(data):
version, visible, unused = read_fmt("IBH", io.BytesIO(data))
return CommonStateInfo(version, bool(visible), unused)
@register(EffectOSType.DROP_SHADOW)
@register(EffectOSType.INNER_SHADOW)
def _decode_shadow_info(data):
fp = io.BytesIO(data)
version, blur, intensity, angle, distance = read_fmt("IIIiI", fp)
color = decode_color(fp)
blend_mode = _read_blend_mode(fp)
enabled, use_global_angle, opacity = read_fmt("3B", fp)
native_color = None
if version == 2:
native_color = decode_color(fp)
return ShadowInfo(
version, bool(enabled),
blend_mode, color, opacity,
angle, bool(use_global_angle),
distance, intensity, blur,
native_color
)
@register(EffectOSType.OUTER_GLOW)
def _decode_outer_glow_info(data):
fp = io.BytesIO(data)
version, blur, intensity = read_fmt("3I", fp)
color = decode_color(fp)
blend_mode = _read_blend_mode(fp)
enabled, opacity = read_fmt("2B", fp)
native_color = None
if version == 2:
native_color = decode_color(fp)
return OuterGlowInfo(
version, bool(enabled),
blend_mode, opacity, color,
intensity, blur,
native_color
)
@register(EffectOSType.INNER_GLOW)
def _decode_inner_glow_info(data):
fp = io.BytesIO(data)
version, blur, intensity = read_fmt("3I", fp)
color = decode_color(fp)
blend_mode = _read_blend_mode(fp)
enabled, opacity = read_fmt("2B", fp)
invert = None
native_color = None
if version == 2:
invert = bool(read_fmt("B", fp)[0])
native_color = decode_color(fp)
return InnerGlowInfo(
version, bool(enabled),
blend_mode, opacity, color,
intensity, blur,
invert, native_color
)
@register(EffectOSType.BEVEL)
def _decode_bevel_info(data):
fp = io.BytesIO(data)
version, angle, depth, blur = read_fmt("IiII", fp)
highlight_blend_mode = _read_blend_mode(fp)
shadow_blend_mode = _read_blend_mode(fp)
highlight_color = decode_color(fp)
shadow_color = decode_color(fp)
bevel_style, highlight_opacity, shadow_opacity = read_fmt("3B", fp)
enabled, use_global_angle, direction = read_fmt("3B", fp)
real_highlight_color = None
real_shadow_color = None
if version == 2:
real_highlight_color = decode_color(fp)
real_shadow_color = decode_color(fp)
return BevelInfo(
version, bool(enabled),
bevel_style,
depth, direction, blur,
angle, bool(use_global_angle),
highlight_blend_mode, highlight_color, highlight_opacity,
shadow_blend_mode, shadow_color, shadow_opacity,
real_highlight_color, real_shadow_color
)
@register(EffectOSType.SOLID_FILL)
def _decode_solid_fill_info(data):
fp = io.BytesIO(data)
version = read_fmt("I", fp)[0]
blend_mode = _read_blend_mode(fp)
color = decode_color(fp)
opacity, enabled = read_fmt("2B", fp)
native_color = decode_color(fp)
return SolidFillInfo(
version, bool(enabled),
blend_mode, color, opacity,
native_color
)
def layer_effects_details(data):
EffectInfo = {}
EffectInfo['SolidFillInfo'] = _decode_solid_fill_info(data)
EffectInfo['shadow_info'] = _decode_shadow_info(data)
EffectInfo['InnerGlowInfo'] = _decode_inner_glow_info(data)
EffectInfo['OuterGlowInfo'] = _decode_outer_glow_info(data)
EffectInfo['BevelInfo'] = _decode_bevel_info(data)
return (EffectInfo)