Skip to content

Commit 6894c35

Browse files
committed
Sync more files
1 parent 993f1be commit 6894c35

File tree

5 files changed

+216
-4
lines changed

5 files changed

+216
-4
lines changed

slyr_community/parser/color_parser.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
Extracts colors from a persistent stream binary
44
"""
55

6+
import math
7+
import itertools
8+
import operator
9+
610
from .color_lut import COLOR_LUT
711

812

@@ -71,6 +75,31 @@ def cielab_to_xyz(l_value, a, b):
7175
return xr * Xr, yr * Yr, zr * Zr
7276

7377

78+
def cielab_to_xyz2(l_value, a, b, illuminants):
79+
"""Translate lab color to xyz, CIM version"""
80+
81+
fy = (l_value + 16) / 116.0
82+
fz = fy - (b / 200.0)
83+
fx = a / 500.0 + fy
84+
85+
e = 216 / 24389 # 0.008856
86+
k = 24389 / 27 # 903.3
87+
if fx**3 > e:
88+
xr = fx**3
89+
else:
90+
xr = (116 * fx - 16) / k
91+
if l_value > k * e:
92+
yr = fy**3
93+
else:
94+
yr = l_value / k
95+
if fz**3 > e:
96+
zr = fz**3
97+
else:
98+
zr = (116 * fz - 16) / k
99+
100+
return xr * illuminants[0], yr * illuminants[1], zr * illuminants[2]
101+
102+
74103
def scale_and_round(r, g, b):
75104
"""Scale to 0-255 and round valued. The algorithm seems to be extremely
76105
precise and equivalent to what is done inside Esri apps, except for
@@ -132,3 +161,151 @@ def cielab_to_rgb(l_value, a, b):
132161

133162
# lab value not present in lookup table, use standard conversion formula
134163
return scale_and_round(*apply_gamma(*xyz_to_rgb(*cielab_to_xyz(l_value, a, b))))
164+
165+
166+
def matrix_multiply_3(m, v):
167+
"""
168+
matrix multiply vector by inner production.
169+
"""
170+
return [
171+
[sum(a * b for a, b in zip(X_row, Y_col)) for Y_col in zip(*v)] for X_row in m
172+
]
173+
174+
175+
def dot(x, y):
176+
"""
177+
Matrix dot product
178+
"""
179+
assert len(x) == len(y)
180+
return sum(itertools.starmap(operator.mul, zip(x, y)))
181+
182+
183+
def matrix_vector_product(m, v):
184+
"""
185+
Matrix vector product
186+
"""
187+
return [dot(row, v) for row in m]
188+
189+
190+
def matrix_invert_3x3(a):
191+
"""
192+
Inverts a 3x3 matrix
193+
"""
194+
c = [
195+
[
196+
a[1][1] * a[2][2] - a[1][2] * a[2][1],
197+
-1 * (a[0][1] * a[2][2] - a[0][2] * a[2][1]),
198+
a[0][1] * a[1][2] - a[0][2] * a[1][1],
199+
],
200+
[
201+
-1 * (a[1][0] * a[2][2] - a[1][2] * a[2][0]),
202+
a[0][0] * a[2][2] - a[0][2] * a[2][0],
203+
-1 * (a[0][0] * a[1][2] - a[0][2] * a[1][0]),
204+
],
205+
[
206+
a[1][0] * a[2][1] - a[1][1] * a[2][0],
207+
-1 * (a[0][0] * a[2][1] - a[0][1] * a[2][0]),
208+
a[0][0] * a[1][1] - a[0][1] * a[1][0],
209+
],
210+
]
211+
212+
b = a[0][0] * c[0][0] + a[0][1] * c[1][0] + a[0][2] * c[2][0]
213+
214+
for d in range(3):
215+
for e in range(3):
216+
c[d][e] = c[d][e] / b
217+
218+
return c
219+
220+
221+
def bradford_transform(a, b):
222+
c = [
223+
[0.8951, 0.2664, -0.1614],
224+
[-0.7502, 1.7135, 0.0367],
225+
[0.0389, -0.0685, 1.0296],
226+
]
227+
f = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
228+
229+
d = matrix_vector_product(c, a)
230+
e = matrix_vector_product(c, b)
231+
232+
for g in range(3):
233+
f[g][g] = e[g] / d[g]
234+
235+
return matrix_multiply_3(matrix_multiply_3(matrix_invert_3x3(c), f), c)
236+
237+
238+
def xyz_chromaticity(a):
239+
return [a[0] / (a[0] + a[1] + a[2]), a[1] / (a[0] + a[1] + a[2])]
240+
241+
242+
def rgb_matrix(a, b, c, d):
243+
e = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
244+
f = [
245+
[a[0] / a[1], b[0] / b[1], c[0] / c[1]],
246+
[1, 1, 1],
247+
[(1 - a[0] - a[1]) / a[1], (1 - b[0] - b[1]) / b[1], (1 - c[0] - c[1]) / c[1]],
248+
]
249+
g = [d[0] / d[1], 1, (1 - d[0] - d[1]) / d[1]]
250+
251+
j = matrix_vector_product(matrix_invert_3x3(f), g)
252+
for h in range(3):
253+
for i in range(3):
254+
e[h][i] = j[i] * f[h][i]
255+
return e
256+
257+
258+
def cielab_to_rgb2(l_value, a, b):
259+
illuminants = [0.9672, 1, 0.81427]
260+
261+
x, y, z = cielab_to_xyz2(l_value, a, b, illuminants)
262+
263+
b = x > illuminants[0] or y > illuminants[1] or z > illuminants[2]
264+
265+
# bc = bradford_transform(illuminants,illuminants)
266+
267+
bt = [
268+
[0.9522842143556952, -0.025006988135028507, 0.06684232012747596],
269+
[-0.030901431979861445, 1.0117439273544555, 0.02228245871328488],
270+
[0.012942977697302113, -0.021511192838354815, 1.348229389280612],
271+
]
272+
273+
x, y, z = matrix_vector_product(bt, [x, y, z])
274+
275+
if not b:
276+
if x > illuminants[0]:
277+
x = illuminants[0]
278+
if y > illuminants[1]:
279+
y = illuminants[1]
280+
if z > illuminants[2]:
281+
z = illuminants[2]
282+
283+
h = (x, y, z)
284+
285+
i = matrix_invert_3x3(
286+
rgb_matrix(
287+
[0.64, 0.33],
288+
[0.3, 0.6],
289+
[0.15, 0.06],
290+
xyz_chromaticity([0.95047, 1, 1.08883]),
291+
)
292+
)
293+
294+
r, g, b = matrix_vector_product(i, h)
295+
i = [r, g, b]
296+
297+
# convert to srgb
298+
for h in range(3):
299+
if i[h] > 0.0031308:
300+
i[h] = 1.055 * (i[h] ** (1 / 2.4)) - 0.055
301+
else:
302+
i[h] = 12.92 * i[h]
303+
i[h] = 255 * i[h] + 0.5
304+
if i[h] < 0:
305+
i[h] = 0
306+
elif i[h] > 255:
307+
i[h] = 255
308+
309+
i[h] = math.floor(i[h])
310+
311+
return i

slyr_community/parser/exceptions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ class DocumentTypeException(Exception):
8282
"""
8383

8484

85+
class CorruptDocumentException(Exception):
86+
"""
87+
Thrown on encountering a corrupt document
88+
"""
89+
90+
8591
class RequiresLicenseException(Exception):
8692
"""
8793
Raised when functionality requires a valid license

slyr_community/parser/object.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,28 @@ def wrapper(*args, **kwargs): # pylint: disable=unused-argument
3131
d["version"] = self.version
3232
if self.ref_id is not None:
3333
d["ref_id"] = self.ref_id
34+
if self.stream_offset is not None:
35+
d["stream_offset"] = hex(self.stream_offset)
36+
37+
def format_dict_value(v):
38+
if isinstance(v, Object):
39+
return v.to_dict()
40+
return v
41+
42+
for k in list(d.keys()):
43+
if isinstance(d[k], dict):
44+
d[k] = {
45+
kk: format_dict_value(vv) for kk, vv in d[k].items()
46+
}
47+
3448
return d
3549

3650
return wrapper
3751

3852
self.to_dict = to_dict_(self.to_dict)
39-
self.version = None
40-
self.ref_id = None
53+
self.version: Optional[int] = None
54+
self.ref_id: Optional[int] = None
55+
self.stream_offset: Optional[int] = None
4156

4257
@staticmethod
4358
def cls_id():

slyr_community/parser/object_registry.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
"""
55

66
from typing import Optional
7-
from .exceptions import UnknownClsidException, CustomExtensionClsidException
7+
from .exceptions import (
8+
UnknownClsidException,
9+
CustomExtensionClsidException,
10+
UnknownObjectTypeException,
11+
)
812
from .object import CustomObject
913

1014

slyr_community/parser/stream.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,14 @@ def read_string(
734734
if terminator is not None and terminator != b"0000":
735735
raise UnreadableSymbolException("Invalid string terminator")
736736

737+
res = ""
738+
for char in string:
739+
if char in ("\ufffe", "\uffff"):
740+
self.log('Found unicode fffe/ffff "not a character", stripping')
741+
else:
742+
res += char
743+
string = res
744+
737745
self.log('found string "{}"'.format(string))
738746

739747
if not self.tolerant and expected is not None:
@@ -870,6 +878,7 @@ def read_object(
870878
"""
871879
Creates and reads a new object from the stream
872880
"""
881+
start = self.tell()
873882
clsid = self.read_clsid(debug_string)
874883
try:
875884
res = REGISTRY.create_object(clsid)
@@ -899,6 +908,7 @@ def read_object(
899908
self.log("{} not found".format(debug_string), 16)
900909

901910
if res is not None:
911+
res.stream_offset = start
902912
self.debug_depth += 1
903913

904914
this_ref = None
@@ -921,7 +931,7 @@ def read_object(
921931
assert res.__class__ == old_res.__class__
922932
self.debug_depth -= 1
923933
return old_res
924-
elif expect_existing and not this_ref - 1 in self.objects[-1]:
934+
elif expect_existing and this_ref - 1 not in self.objects[-1]:
925935
self.log(
926936
"Expecting an existing object with ref {}, but not yet created".format(
927937
this_ref

0 commit comments

Comments
 (0)