Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4680 | right-hear | 1 | #include "fitz.h" |
2 | #include "mupdf.h" |
||
3 | |||
4 | /* ICCBased */ |
||
5 | |||
6 | static fz_error |
||
7 | load_icc_based(fz_colorspace **csp, pdf_xref *xref, fz_obj *dict) |
||
8 | { |
||
9 | int n; |
||
10 | |||
11 | n = fz_to_int(fz_dict_gets(dict, "N")); |
||
12 | |||
13 | switch (n) |
||
14 | { |
||
15 | case 1: *csp = fz_device_gray; return fz_okay; |
||
16 | case 3: *csp = fz_device_rgb; return fz_okay; |
||
17 | case 4: *csp = fz_device_cmyk; return fz_okay; |
||
18 | } |
||
19 | |||
20 | return fz_throw("syntaxerror: ICCBased must have 1, 3 or 4 components"); |
||
21 | } |
||
22 | |||
23 | /* Lab */ |
||
24 | |||
25 | static inline float fung(float x) |
||
26 | { |
||
27 | if (x >= 6.0f / 29.0f) |
||
28 | return x * x * x; |
||
29 | return (108.0f / 841.0f) * (x - (4.0f / 29.0f)); |
||
30 | } |
||
31 | |||
32 | static void |
||
33 | lab_to_rgb(fz_colorspace *cs, float *lab, float *rgb) |
||
34 | { |
||
35 | /* input is in range (0..100, -128..127, -128..127) not (0..1, 0..1, 0..1) */ |
||
36 | float lstar, astar, bstar, l, m, n, x, y, z, r, g, b; |
||
37 | lstar = lab[0]; |
||
38 | astar = lab[1]; |
||
39 | bstar = lab[2]; |
||
40 | m = (lstar + 16) / 116; |
||
41 | l = m + astar / 500; |
||
42 | n = m - bstar / 200; |
||
43 | x = fung(l); |
||
44 | y = fung(m); |
||
45 | z = fung(n); |
||
46 | r = (3.240449f * x + -1.537136f * y + -0.498531f * z) * 0.830026f; |
||
47 | g = (-0.969265f * x + 1.876011f * y + 0.041556f * z) * 1.05452f; |
||
48 | b = (0.055643f * x + -0.204026f * y + 1.057229f * z) * 1.1003f; |
||
49 | rgb[0] = sqrtf(CLAMP(r, 0, 1)); |
||
50 | rgb[1] = sqrtf(CLAMP(g, 0, 1)); |
||
51 | rgb[2] = sqrtf(CLAMP(b, 0, 1)); |
||
52 | } |
||
53 | |||
54 | static void |
||
55 | rgb_to_lab(fz_colorspace *cs, float *rgb, float *lab) |
||
56 | { |
||
57 | fz_warn("cannot convert into L*a*b colorspace"); |
||
58 | lab[0] = rgb[0]; |
||
59 | lab[1] = rgb[1]; |
||
60 | lab[2] = rgb[2]; |
||
61 | } |
||
62 | |||
63 | static fz_colorspace k_device_lab = { -1, "Lab", 3, lab_to_rgb, rgb_to_lab }; |
||
64 | static fz_colorspace *fz_device_lab = &k_device_lab; |
||
65 | |||
66 | /* Separation and DeviceN */ |
||
67 | |||
68 | struct separation |
||
69 | { |
||
70 | fz_colorspace *base; |
||
71 | pdf_function *tint; |
||
72 | }; |
||
73 | |||
74 | static void |
||
75 | separation_to_rgb(fz_colorspace *cs, float *color, float *rgb) |
||
76 | { |
||
77 | struct separation *sep = cs->data; |
||
78 | float alt[FZ_MAX_COLORS]; |
||
79 | pdf_eval_function(sep->tint, color, cs->n, alt, sep->base->n); |
||
80 | sep->base->to_rgb(sep->base, alt, rgb); |
||
81 | } |
||
82 | |||
83 | static void |
||
84 | free_separation(fz_colorspace *cs) |
||
85 | { |
||
86 | struct separation *sep = cs->data; |
||
87 | fz_drop_colorspace(sep->base); |
||
88 | pdf_drop_function(sep->tint); |
||
89 | fz_free(sep); |
||
90 | } |
||
91 | |||
92 | static fz_error |
||
93 | load_separation(fz_colorspace **csp, pdf_xref *xref, fz_obj *array) |
||
94 | { |
||
95 | fz_error error; |
||
96 | fz_colorspace *cs; |
||
97 | struct separation *sep; |
||
98 | fz_obj *nameobj = fz_array_get(array, 1); |
||
99 | fz_obj *baseobj = fz_array_get(array, 2); |
||
100 | fz_obj *tintobj = fz_array_get(array, 3); |
||
101 | fz_colorspace *base; |
||
102 | pdf_function *tint; |
||
103 | int n; |
||
104 | |||
105 | if (fz_is_array(nameobj)) |
||
106 | n = fz_array_len(nameobj); |
||
107 | else |
||
108 | n = 1; |
||
109 | |||
110 | if (n > FZ_MAX_COLORS) |
||
111 | return fz_throw("too many components in colorspace"); |
||
112 | |||
113 | error = pdf_load_colorspace(&base, xref, baseobj); |
||
114 | if (error) |
||
115 | return fz_rethrow(error, "cannot load base colorspace (%d %d R)", fz_to_num(baseobj), fz_to_gen(baseobj)); |
||
116 | |||
117 | error = pdf_load_function(&tint, xref, tintobj); |
||
118 | if (error) |
||
119 | { |
||
120 | fz_drop_colorspace(base); |
||
121 | return fz_rethrow(error, "cannot load tint function (%d %d R)", fz_to_num(tintobj), fz_to_gen(tintobj)); |
||
122 | } |
||
123 | |||
124 | sep = fz_malloc(sizeof(struct separation)); |
||
125 | sep->base = base; |
||
126 | sep->tint = tint; |
||
127 | |||
128 | cs = fz_new_colorspace(n == 1 ? "Separation" : "DeviceN", n); |
||
129 | cs->to_rgb = separation_to_rgb; |
||
130 | cs->free_data = free_separation; |
||
131 | cs->data = sep; |
||
132 | |||
133 | *csp = cs; |
||
134 | return fz_okay; |
||
135 | } |
||
136 | |||
137 | /* Indexed */ |
||
138 | |||
139 | struct indexed |
||
140 | { |
||
141 | fz_colorspace *base; |
||
142 | int high; |
||
143 | unsigned char *lookup; |
||
144 | }; |
||
145 | |||
146 | static void |
||
147 | indexed_to_rgb(fz_colorspace *cs, float *color, float *rgb) |
||
148 | { |
||
149 | struct indexed *idx = cs->data; |
||
150 | float alt[FZ_MAX_COLORS]; |
||
151 | int i, k; |
||
152 | i = color[0] * 255; |
||
153 | i = CLAMP(i, 0, idx->high); |
||
154 | for (k = 0; k < idx->base->n; k++) |
||
155 | alt[k] = idx->lookup[i * idx->base->n + k] / 255.0f; |
||
156 | idx->base->to_rgb(idx->base, alt, rgb); |
||
157 | } |
||
158 | |||
159 | static void |
||
160 | free_indexed(fz_colorspace *cs) |
||
161 | { |
||
162 | struct indexed *idx = cs->data; |
||
163 | if (idx->base) |
||
164 | fz_drop_colorspace(idx->base); |
||
165 | fz_free(idx->lookup); |
||
166 | fz_free(idx); |
||
167 | } |
||
168 | |||
169 | fz_pixmap * |
||
170 | pdf_expand_indexed_pixmap(fz_pixmap *src) |
||
171 | { |
||
172 | struct indexed *idx; |
||
173 | fz_pixmap *dst; |
||
174 | unsigned char *s, *d; |
||
175 | int y, x, k, n, high; |
||
176 | unsigned char *lookup; |
||
177 | |||
178 | assert(src->colorspace->to_rgb == indexed_to_rgb); |
||
179 | assert(src->n == 2); |
||
180 | |||
181 | idx = src->colorspace->data; |
||
182 | high = idx->high; |
||
183 | lookup = idx->lookup; |
||
184 | n = idx->base->n; |
||
185 | |||
186 | dst = fz_new_pixmap_with_rect(idx->base, fz_bound_pixmap(src)); |
||
187 | s = src->samples; |
||
188 | d = dst->samples; |
||
189 | |||
190 | for (y = 0; y < src->h; y++) |
||
191 | { |
||
192 | for (x = 0; x < src->w; x++) |
||
193 | { |
||
194 | int v = *s++; |
||
195 | int a = *s++; |
||
196 | v = MIN(v, high); |
||
197 | for (k = 0; k < n; k++) |
||
198 | *d++ = fz_mul255(lookup[v * n + k], a); |
||
199 | *d++ = a; |
||
200 | } |
||
201 | } |
||
202 | |||
203 | if (src->mask) |
||
204 | dst->mask = fz_keep_pixmap(src->mask); |
||
205 | dst->interpolate = src->interpolate; |
||
206 | |||
207 | return dst; |
||
208 | } |
||
209 | |||
210 | static fz_error |
||
211 | load_indexed(fz_colorspace **csp, pdf_xref *xref, fz_obj *array) |
||
212 | { |
||
213 | fz_error error; |
||
214 | fz_colorspace *cs; |
||
215 | struct indexed *idx; |
||
216 | fz_obj *baseobj = fz_array_get(array, 1); |
||
217 | fz_obj *highobj = fz_array_get(array, 2); |
||
218 | fz_obj *lookup = fz_array_get(array, 3); |
||
219 | fz_colorspace *base; |
||
220 | int i, n; |
||
221 | |||
222 | error = pdf_load_colorspace(&base, xref, baseobj); |
||
223 | if (error) |
||
224 | return fz_rethrow(error, "cannot load base colorspace (%d %d R)", fz_to_num(baseobj), fz_to_gen(baseobj)); |
||
225 | |||
226 | idx = fz_malloc(sizeof(struct indexed)); |
||
227 | idx->base = base; |
||
228 | idx->high = fz_to_int(highobj); |
||
229 | idx->high = CLAMP(idx->high, 0, 255); |
||
230 | n = base->n * (idx->high + 1); |
||
231 | idx->lookup = fz_malloc(n); |
||
232 | memset(idx->lookup, 0, n); |
||
233 | |||
234 | cs = fz_new_colorspace("Indexed", 1); |
||
235 | cs->to_rgb = indexed_to_rgb; |
||
236 | cs->free_data = free_indexed; |
||
237 | cs->data = idx; |
||
238 | |||
239 | if (fz_is_string(lookup) && fz_to_str_len(lookup) == n) |
||
240 | { |
||
241 | unsigned char *buf = (unsigned char *) fz_to_str_buf(lookup); |
||
242 | for (i = 0; i < n; i++) |
||
243 | idx->lookup[i] = buf[i]; |
||
244 | } |
||
245 | else if (fz_is_indirect(lookup)) |
||
246 | { |
||
247 | fz_stream *file; |
||
248 | |||
249 | error = pdf_open_stream(&file, xref, fz_to_num(lookup), fz_to_gen(lookup)); |
||
250 | if (error) |
||
251 | { |
||
252 | fz_drop_colorspace(cs); |
||
253 | return fz_rethrow(error, "cannot open colorspace lookup table (%d 0 R)", fz_to_num(lookup)); |
||
254 | } |
||
255 | |||
256 | i = fz_read(file, idx->lookup, n); |
||
257 | if (i < 0) |
||
258 | { |
||
259 | fz_drop_colorspace(cs); |
||
260 | return fz_throw("cannot read colorspace lookup table (%d 0 R)", fz_to_num(lookup)); |
||
261 | } |
||
262 | |||
263 | fz_close(file); |
||
264 | } |
||
265 | else |
||
266 | { |
||
267 | fz_drop_colorspace(cs); |
||
268 | return fz_throw("cannot parse colorspace lookup table"); |
||
269 | } |
||
270 | |||
271 | *csp = cs; |
||
272 | return fz_okay; |
||
273 | } |
||
274 | |||
275 | /* Parse and create colorspace from PDF object */ |
||
276 | |||
277 | static fz_error |
||
278 | pdf_load_colorspace_imp(fz_colorspace **csp, pdf_xref *xref, fz_obj *obj) |
||
279 | { |
||
280 | if (fz_is_name(obj)) |
||
281 | { |
||
282 | if (!strcmp(fz_to_name(obj), "Pattern")) |
||
283 | *csp = fz_device_gray; |
||
284 | else if (!strcmp(fz_to_name(obj), "G")) |
||
285 | *csp = fz_device_gray; |
||
286 | else if (!strcmp(fz_to_name(obj), "RGB")) |
||
287 | *csp = fz_device_rgb; |
||
288 | else if (!strcmp(fz_to_name(obj), "CMYK")) |
||
289 | *csp = fz_device_cmyk; |
||
290 | else if (!strcmp(fz_to_name(obj), "DeviceGray")) |
||
291 | *csp = fz_device_gray; |
||
292 | else if (!strcmp(fz_to_name(obj), "DeviceRGB")) |
||
293 | *csp = fz_device_rgb; |
||
294 | else if (!strcmp(fz_to_name(obj), "DeviceCMYK")) |
||
295 | *csp = fz_device_cmyk; |
||
296 | else |
||
297 | return fz_throw("unknown colorspace: %s", fz_to_name(obj)); |
||
298 | return fz_okay; |
||
299 | } |
||
300 | |||
301 | else if (fz_is_array(obj)) |
||
302 | { |
||
303 | fz_obj *name = fz_array_get(obj, 0); |
||
304 | |||
305 | if (fz_is_name(name)) |
||
306 | { |
||
307 | /* load base colorspace instead */ |
||
308 | if (!strcmp(fz_to_name(name), "Pattern")) |
||
309 | { |
||
310 | fz_error error; |
||
311 | |||
312 | obj = fz_array_get(obj, 1); |
||
313 | if (!obj) |
||
314 | { |
||
315 | *csp = fz_device_gray; |
||
316 | return fz_okay; |
||
317 | } |
||
318 | |||
319 | error = pdf_load_colorspace(csp, xref, obj); |
||
320 | if (error) |
||
321 | return fz_rethrow(error, "cannot load pattern (%d %d R)", fz_to_num(obj), fz_to_gen(obj)); |
||
322 | } |
||
323 | |||
324 | else if (!strcmp(fz_to_name(name), "G")) |
||
325 | *csp = fz_device_gray; |
||
326 | else if (!strcmp(fz_to_name(name), "RGB")) |
||
327 | *csp = fz_device_rgb; |
||
328 | else if (!strcmp(fz_to_name(name), "CMYK")) |
||
329 | *csp = fz_device_cmyk; |
||
330 | else if (!strcmp(fz_to_name(name), "DeviceGray")) |
||
331 | *csp = fz_device_gray; |
||
332 | else if (!strcmp(fz_to_name(name), "DeviceRGB")) |
||
333 | *csp = fz_device_rgb; |
||
334 | else if (!strcmp(fz_to_name(name), "DeviceCMYK")) |
||
335 | *csp = fz_device_cmyk; |
||
336 | else if (!strcmp(fz_to_name(name), "CalGray")) |
||
337 | *csp = fz_device_gray; |
||
338 | else if (!strcmp(fz_to_name(name), "CalRGB")) |
||
339 | *csp = fz_device_rgb; |
||
340 | else if (!strcmp(fz_to_name(name), "CalCMYK")) |
||
341 | *csp = fz_device_cmyk; |
||
342 | else if (!strcmp(fz_to_name(name), "Lab")) |
||
343 | *csp = fz_device_lab; |
||
344 | |||
345 | else if (!strcmp(fz_to_name(name), "ICCBased")) |
||
346 | return load_icc_based(csp, xref, fz_array_get(obj, 1)); |
||
347 | |||
348 | else if (!strcmp(fz_to_name(name), "Indexed")) |
||
349 | return load_indexed(csp, xref, obj); |
||
350 | else if (!strcmp(fz_to_name(name), "I")) |
||
351 | return load_indexed(csp, xref, obj); |
||
352 | |||
353 | else if (!strcmp(fz_to_name(name), "Separation")) |
||
354 | return load_separation(csp, xref, obj); |
||
355 | |||
356 | else if (!strcmp(fz_to_name(name), "DeviceN")) |
||
357 | return load_separation(csp, xref, obj); |
||
358 | |||
359 | else |
||
360 | return fz_throw("syntaxerror: unknown colorspace %s", fz_to_name(name)); |
||
361 | |||
362 | return fz_okay; |
||
363 | } |
||
364 | } |
||
365 | |||
366 | return fz_throw("syntaxerror: could not parse color space (%d %d R)", fz_to_num(obj), fz_to_gen(obj)); |
||
367 | } |
||
368 | |||
369 | fz_error |
||
370 | pdf_load_colorspace(fz_colorspace **csp, pdf_xref *xref, fz_obj *obj) |
||
371 | { |
||
372 | fz_error error; |
||
373 | |||
374 | if ((*csp = pdf_find_item(xref->store, fz_drop_colorspace, obj))) |
||
375 | { |
||
376 | fz_keep_colorspace(*csp); |
||
377 | return fz_okay; |
||
378 | } |
||
379 | |||
380 | error = pdf_load_colorspace_imp(csp, xref, obj); |
||
381 | if (error) |
||
382 | return fz_rethrow(error, "cannot load colorspace (%d %d R)", fz_to_num(obj), fz_to_gen(obj)); |
||
383 | |||
384 | pdf_store_item(xref->store, fz_keep_colorspace, fz_drop_colorspace, obj, *csp); |
||
385 | |||
386 | return fz_okay; |
||
387 | }>>>>>> |