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
166
167
168local default = { 96.422, 100, 82.521 }
169
170
171local function xyztolab(x,y,z,mapping)
172 if not mapping then
173 mapping = default
174 end
175 x = x / mapping[1]
176 y = y / mapping[2]
177 z = z / mapping[3]
178 x = (x > 0.008856) and x^(1/3) or (7.787 * x) + (16/116)
179 y = (y > 0.008856) and y^(1/3) or (7.787 * y) + (16/116)
180 z = (z > 0.008856) and z^(1/3) or (7.787 * z) + (16/116)
181 return
182 116 * y - 16,
183 500 * (x - y),
184 200 * (y - z)
185end
186
187local function labtoxyz(l,a,b,mapping)
188 if not mapping then
189 mapping = default
190 end
191 local y = (l + 16) / 116
192 local x = a / 500 + y
193 local z = y - b / 200
194 return
195 mapping[1] * ((x^3 > 0.008856) and x^3 or (x - 16/116) / 7.787),
196 mapping[2] * ((y^3 > 0.008856) and y^3 or (y - 16/116) / 7.787),
197 mapping[3] * ((z^3 > 0.008856) and z^3 or (z - 16/116) / 7.787)
198end
199
200local function xyztorgb(x,y,z)
201
202
203
204
205
206 local r = (x * 3.1338561 + y * -1.6168667 + z * -0.4906146) / 100
207 local g = (x * -0.9787684 + y * 1.9161415 + z * 0.0334540) / 100
208 local b = (x * 0.0719453 + y * -0.2289914 + z * 1.4052427) / 100
209
210 r = (r > 0.0031308) and (1.055 * r^(1/2.4) - 0.055) or (12.92 * r)
211 g = (g > 0.0031308) and (1.055 * g^(1/2.4) - 0.055) or (12.92 * g)
212 b = (b > 0.0031308) and (1.055 * b^(1/2.4) - 0.055) or (12.92 * b)
213 if r < 0 then r = 0 elseif r > 1 then r = 1 end
214 if g < 0 then g = 0 elseif g > 1 then g = 1 end
215 if b < 0 then b = 0 elseif b > 1 then b = 1 end
216 return r, g, b
217end
218
219local function rgbtoxyz(r,g,b)
220 r = 100 * ((r > 0.04045) and ((r + 0.055)/1.055)^2.4 or (r / 12.92))
221 g = 100 * ((g > 0.04045) and ((g + 0.055)/1.055)^2.4 or (g / 12.92))
222 b = 100 * ((b > 0.04045) and ((b + 0.055)/1.055)^2.4 or (b / 12.92))
223 return
224 r * 0.4124 + g * 0.3576 + b * 0.1805,
225 r * 0.2126 + g * 0.7152 + b * 0.0722,
226 r * 0.0193 + g * 0.1192 + b * 0.9505
227end
228
229local function labtorgb(l,a,b,mapping)
230 return xyztorgb(labtoxyz(l,a,b,mapping))
231end
232
233colors.xyztolab = xyztolab
234colors.labtoxyz = labtoxyz
235colors.xyztorgb = xyztorgb
236colors.rgbtoxyz = rgbtoxyz
237colors.labtorgb = labtorgb
238 |