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 next = next
12
13local colors = attributes and attributes.colors or { }
14
15local report_colors = logs.reporter("colors","icc")
16
17local R, Cs, lpegmatch = lpeg.R, lpeg.Cs, lpeg.match
18
19local invalid = R(char(0)..char(31))
20local cleaned = invalid^0 * Cs((1-invalid)^0)
21
22function colors.iccprofile(filename,verbose)
23 local fullname = resolvers.findfile(filename,"icc") or ""
24 if fullname == "" then
25 local locate = resolvers.finders.byscheme
26 if locate then
27 fullname = locate("loc",filename)
28 end
29 end
30 if fullname == "" then
31 report_colors("profile %a cannot be found",filename)
32 return nil, false
33 end
34 local f = io.open(fullname,"rb")
35 if not f then
36 report_colors("profile %a cannot be loaded",fullname)
37 return nil, false
38 end
39 local header = {
40 size = readnumber(f,4),
41 cmmtype = readnumber(f,4),
42 version = readnumber(f,4),
43 deviceclass = strip(readstring(f,4)),
44 colorspace = strip(readstring(f,4)),
45 connectionspace = strip(readstring(f,4)),
46 datetime = {
47 year = readnumber(f,2),
48 month = readnumber(f,2),
49 day = readnumber(f,2),
50 hour = readnumber(f,2),
51 minutes = readnumber(f,2),
52 seconds = readnumber(f,2),
53 },
54 filesignature = strip(readstring(f,4)),
55 platformsignature = strip(readstring(f,4)),
56 options = readnumber(f,4),
57 devicemanufacturer = strip(readstring(f,4)),
58 devicemodel = strip(readstring(f,4)),
59 deviceattributes = readnumber(f,4),
60 renderingintent = readnumber(f,4),
61 illuminantxyz = {
62 x = readnumber(f,4),
63 y = readnumber(f,4),
64 z = readnumber(f,4),
65 },
66 profilecreator = readnumber(f,4),
67 id = strip(readstring(f,16)),
68 }
69 local tags = { }
70 for i=1,readnumber(f,128,4) do
71 tags[readstring(f,4)] = {
72 offset = readnumber(f,4),
73 length = readnumber(f,4),
74 }
75 end
76 local o = header.options
77 header.options =
78 o == 0 and "embedded" or
79 o == 1 and "dependent" or "unknown"
80 local d = header.deviceattributes
81 header.deviceattributes = {
82 [(d & 0x01) ~= 0 and "transparency" or "reflective"] = true,
83 [(d & 0x02) ~= 0 and "mate" or "glossy" ] = true,
84 [(d & 0x04) ~= 0 and "negative" or "positive" ] = true,
85 [(d & 0x08) ~= 0 and "bw" or "color" ] = true,
86 }
87 local r = header.renderingintent
88 header.renderingintent =
89 r == 0 and "perceptual" or
90 r == 1 and "relative" or
91 r == 2 and "saturation" or
92 r == 3 and "absolute" or "unknown"
93 for tag, spec in next, tags do
94 if tag then
95 local offset, length = spec.offset, spec.length
96 local variant = readstring(f,offset,4)
97 if variant == "text" or variant == "desc" then
98 local str = readstring(f,length-4)
99 tags[tag] = {
100 data = str,
101 cleaned = lpegmatch(cleaned,str),
102 }
103 else
104 if verbose then
105 report_colors("ignoring tag %a or type %a in profile %a",tag,variant,fullname)
106 end
107 tags[tag] = nil
108 end
109 end
110 end
111 f:close()
112 local profile = {
113 filename = filename,
114 fullname = fullname,
115 header = header,
116 tags = tags,
117 }
118 report_colors("profile %a loaded",fullname)
119 return profile, true
120end
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
167local default = { 96.422, 100, 82.521 }
168
169
170local function xyztolab(x,y,z,mapping)
171 if not mapping then
172 mapping = default
173 end
174 x = x / mapping[1]
175 y = y / mapping[2]
176 z = z / mapping[3]
177 x = (x > 0.008856) and x^(1/3) or (7.787 * x) + (16/116)
178 y = (y > 0.008856) and y^(1/3) or (7.787 * y) + (16/116)
179 z = (z > 0.008856) and z^(1/3) or (7.787 * z) + (16/116)
180 return
181 116 * y - 16,
182 500 * (x - y),
183 200 * (y - z)
184end
185
186local function labtoxyz(l,a,b,mapping)
187 if not mapping then
188 mapping = default
189 end
190 local y = (l + 16) / 116
191 local x = a / 500 + y
192 local z = y - b / 200
193 return
194 mapping[1] * ((x^3 > 0.008856) and x^3 or (x - 16/116) / 7.787),
195 mapping[2] * ((y^3 > 0.008856) and y^3 or (y - 16/116) / 7.787),
196 mapping[3] * ((z^3 > 0.008856) and z^3 or (z - 16/116) / 7.787)
197end
198
199local function xyztorgb(x,y,z)
200
201
202
203
204
205 local r = (x * 3.1338561 + y * -1.6168667 + z * -0.4906146) / 100
206 local g = (x * -0.9787684 + y * 1.9161415 + z * 0.0334540) / 100
207 local b = (x * 0.0719453 + y * -0.2289914 + z * 1.4052427) / 100
208
209 r = (r > 0.0031308) and (1.055 * r^(1/2.4) - 0.055) or (12.92 * r)
210 g = (g > 0.0031308) and (1.055 * g^(1/2.4) - 0.055) or (12.92 * g)
211 b = (b > 0.0031308) and (1.055 * b^(1/2.4) - 0.055) or (12.92 * b)
212 if r < 0 then r = 0 elseif r > 1 then r = 1 end
213 if g < 0 then g = 0 elseif g > 1 then g = 1 end
214 if b < 0 then b = 0 elseif b > 1 then b = 1 end
215 return r, g, b
216end
217
218local function rgbtoxyz(r,g,b)
219 r = 100 * ((r > 0.04045) and ((r + 0.055)/1.055)^2.4 or (r / 12.92))
220 g = 100 * ((g > 0.04045) and ((g + 0.055)/1.055)^2.4 or (g / 12.92))
221 b = 100 * ((b > 0.04045) and ((b + 0.055)/1.055)^2.4 or (b / 12.92))
222 return
223 r * 0.4124 + g * 0.3576 + b * 0.1805,
224 r * 0.2126 + g * 0.7152 + b * 0.0722,
225 r * 0.0193 + g * 0.1192 + b * 0.9505
226end
227
228local function labtorgb(l,a,b,mapping)
229 return xyztorgb(labtoxyz(l,a,b,mapping))
230end
231
232colors.xyztolab = xyztolab
233colors.labtoxyz = labtoxyz
234colors.xyztorgb = xyztorgb
235colors.rgbtoxyz = rgbtoxyz
236colors.labtorgb = labtorgb
237
238
239
240
241
242local sin, cos, atan2, sqrt = math.sin, math.cos, math.atan2, math.sqrt
243
244local pi <const> = math.pi
245
246local function labtolch(l,a,b)
247 local h = atan2(b,a) * 180/pi
248 local c = sqrt(a^2 + b^2)
249 return l, c, h >= 0 and h or (h + 360)
250end
251
252local function lchtolab(l,c,h)
253 local v = c * pi/180
254 local a = h * cos(v)
255 local b = h * sin(v)
256 return l, a, b
257end
258
259local function lchtorgb(l,c,h)
260 local l, a, b = lchtolab(l,h,c)
261 local x, y, z = labtoxyz(l,a,b)
262 return xyztorgb(x,y,z)
263end
264
265colors.labtolch = labtolch
266colors.lchtolab = lchtolab
267colors.lchtorgb = lchtorgb
268 |