1if not modules then modules = { } end modules ['colo-icc'] = {
2 version = 1.000,
3 comment = "companion to colo-ini.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local char, byte, gsub, match, format, strip = string.char, string.byte, string.gsub, string.match, string.format, string.strip
10local readstring, readnumber = io.readstring, io.readnumber
11local band = bit32.band
12local next = next
13
14local colors = attributes and attributes.colors or { }
15
16local report_colors = logs.reporter("colors","icc")
17
18local R, Cs, lpegmatch = lpeg.R, lpeg.Cs, lpeg.match
19
20local invalid = R(char(0)..char(31))
21local cleaned = invalid^0 * Cs((1-invalid)^0)
22
23function colors.iccprofile(filename,verbose)
24 local fullname = resolvers.findfile(filename,"icc") or ""
25 if fullname == "" then
26 local locate = resolvers.finders.byscheme
27 if locate then
28 fullname = locate("loc",filename)
29 end
30 end
31 if fullname == "" then
32 report_colors("profile %a cannot be found",filename)
33 return nil, false
34 end
35 local f = io.open(fullname,"rb")
36 if not f then
37 report_colors("profile %a cannot be loaded",fullname)
38 return nil, false
39 end
40 local header = {
41 size = readnumber(f,4),
42 cmmtype = readnumber(f,4),
43 version = readnumber(f,4),
44 deviceclass = strip(readstring(f,4)),
45 colorspace = strip(readstring(f,4)),
46 connectionspace = strip(readstring(f,4)),
47 datetime = {
48 year = readnumber(f,2),
49 month = readnumber(f,2),
50 day = readnumber(f,2),
51 hour = readnumber(f,2),
52 minutes = readnumber(f,2),
53 seconds = readnumber(f,2),
54 },
55 filesignature = strip(readstring(f,4)),
56 platformsignature = strip(readstring(f,4)),
57 options = readnumber(f,4),
58 devicemanufacturer = strip(readstring(f,4)),
59 devicemodel = strip(readstring(f,4)),
60 deviceattributes = readnumber(f,4),
61 renderingintent = readnumber(f,4),
62 illuminantxyz = {
63 x = readnumber(f,4),
64 y = readnumber(f,4),
65 z = readnumber(f,4),
66 },
67 profilecreator = readnumber(f,4),
68 id = strip(readstring(f,16)),
69 }
70 local tags = { }
71 for i=1,readnumber(f,128,4) do
72 tags[readstring(f,4)] = {
73 offset = readnumber(f,4),
74 length = readnumber(f,4),
75 }
76 end
77 local o = header.options
78 header.options =
79 o == 0 and "embedded" or
80 o == 1 and "dependent" or "unknown"
81 local d = header.deviceattributes
82 header.deviceattributes = {
83 [band(d,1) ~= 0 and "transparency" or "reflective"] = true,
84 [band(d,2) ~= 0 and "mate" or "glossy" ] = true,
85 [band(d,3) ~= 0 and "negative" or "positive" ] = true,
86 [band(d,4) ~= 0 and "bw" or "color" ] = true,
87 }
88 local r = header.renderingintent
89 header.renderingintent =
90 r == 0 and "perceptual" or
91 r == 1 and "relative" or
92 r == 2 and "saturation" or
93 r == 3 and "absolute" or "unknown"
94 for tag, spec in next, tags do
95 if tag then
96 local offset, length = spec.offset, spec.length
97 local variant = readstring(f,offset,4)
98 if variant == "text" or variant == "desc" then
99 local str = readstring(f,length-4)
100 tags[tag] = {
101 data = str,
102 cleaned = lpegmatch(cleaned,str),
103 }
104 else
105 if verbose then
106 report_colors("ignoring tag %a or type %a in profile %a",tag,variant,fullname)
107 end
108 tags[tag] = nil
109 end
110 end
111 end
112 f:close()
113 local profile = {
114 filename = filename,
115 fullname = fullname,
116 header = header,
117 tags = tags,
118 }
119 report_colors("profile %a loaded",fullname)
120 return profile, true
121end
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
166local D652 = { 95.047, 100, 108.883 }
167
168local function xyztolab(x,y,z,mapping)
169 if not mapping then
170 mapping = D652
171 end
172 x = x / mapping[1]
173 y = y / mapping[2]
174 z = z / mapping[3]
175 x = (x > 0.008856) and x^(1/3) or (7.787 * x) + (16/116)
176 y = (y > 0.008856) and y^(1/3) or (7.787 * y) + (16/116)
177 z = (z > 0.008856) and z^(1/3) or (7.787 * z) + (16/116)
178 return
179 116 * y - 16,
180 500 * (x - y),
181 200 * (y - z)
182end
183
184local function labtoxyz(l,a,b,mapping)
185 if not mapping then
186 mapping = D652
187 end
188 local y = (l + 16) / 116
189 local x = a / 500 + y
190 local z = y - b / 200
191 return
192 mapping[1] * ((x^3 > 0.008856) and x^3 or (x - 16/116) / 7.787),
193 mapping[2] * ((y^3 > 0.008856) and y^3 or (y - 16/116) / 7.787),
194 mapping[3] * ((z^3 > 0.008856) and z^3 or (z - 16/116) / 7.787)
195end
196
197local function xyztorgb(x,y,z)
198 local r = (x * 3.2404542 + y * -1.5371385 + z * -0.4985314) / 100
199 local g = (x * -0.9692660 + y * 1.8760108 + z * 0.0415560) / 100
200 local b = (x * 0.0556434 + y * -0.2040259 + z * 1.0572252) / 100
201 r = (r > 0.0031308) and (1.055 * r^(1/2.4) - 0.055) or (12.92 * r)
202 g = (g > 0.0031308) and (1.055 * g^(1/2.4) - 0.055) or (12.92 * g)
203 b = (b > 0.0031308) and (1.055 * b^(1/2.4) - 0.055) or (12.92 * b)
204 if r < 0 then r = 0 elseif r > 1 then r = 1 end
205 if g < 0 then g = 0 elseif g > 1 then g = 1 end
206 if b < 0 then b = 0 elseif b > 1 then b = 1 end
207 return r, g, b
208end
209
210local function rgbtoxyz(r,g,b)
211 r = 100 * ((r > 0.04045) and ((r + 0.055)/1.055)^2.4 or (r / 12.92))
212 g = 100 * ((g > 0.04045) and ((g + 0.055)/1.055)^2.4 or (g / 12.92))
213 b = 100 * ((b > 0.04045) and ((b + 0.055)/1.055)^2.4 or (b / 12.92))
214 return
215 r * 0.4124 + g * 0.3576 + b * 0.1805,
216 r * 0.2126 + g * 0.7152 + b * 0.0722,
217 r * 0.0193 + g * 0.1192 + b * 0.9505
218end
219
220local function labtorgb(l,a,b,mapping)
221 return xyztorgb(labtoxyz(l,a,b,mapping))
222end
223
224colors.xyztolab = xyztolab
225colors.labtoxyz = labtoxyz
226colors.xyztorgb = xyztorgb
227colors.rgbtoxyz = rgbtoxyz
228colors.labtorgb = labtorgb
229 |