comparison mupdf-source/source/pdf/pdf-shade-recolor.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children aa33339d6b8a
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2025 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24 #include "mupdf/pdf.h"
25
26 #include <string.h>
27 #include <math.h>
28 #include <float.h>
29
30 typedef struct
31 {
32 void *opaque;
33 pdf_recolor_vertex *recolor;
34 fz_colorspace *dst_cs;
35 fz_colorspace *src_cs;
36 int funcs;
37 } recolor_details;
38
39 #define FUNSEGS 64 /* size of sampled mesh for function-based shadings */
40 #define FUNBPS 8 /* Bits per sample in output functions */
41
42 static void
43 fz_recolor_shade_type1(fz_context *ctx, pdf_obj *shade, pdf_function **func, recolor_details *rd)
44 {
45 float x0 = 0;
46 float y0 = 0;
47 float x1 = 1;
48 float y1 = 1;
49 float in[FZ_MAX_COLORS] = { 0 };
50 float out[(FUNSEGS+1)*(FUNSEGS+1)*FZ_MAX_COLORS];
51 float *p;
52 float fv[2];
53 int xx, yy;
54 pdf_obj *obj;
55 int n_in = rd->src_cs->n;
56 int n_out = rd->dst_cs->n;
57 pdf_obj *fun_obj = NULL;
58 float range[FZ_MAX_COLORS];
59 int i;
60 pdf_document *doc = pdf_get_bound_document(ctx, shade);
61 pdf_obj *ref = NULL;
62 fz_buffer *buf = NULL;
63 fz_output *output = NULL;
64
65 obj = pdf_dict_get(ctx, shade, PDF_NAME(Domain));
66 if (obj)
67 {
68 x0 = pdf_array_get_real(ctx, obj, 0);
69 x1 = pdf_array_get_real(ctx, obj, 1);
70 y0 = pdf_array_get_real(ctx, obj, 2);
71 y1 = pdf_array_get_real(ctx, obj, 3);
72 }
73
74 if (rd->funcs != 1 && rd->funcs != n_in)
75 {
76 fz_throw(ctx, FZ_ERROR_SYNTAX, "Unexpected function-arity.");
77 }
78
79 /* Sample the function, rewriting it. */
80 for (i = 0; i < n_out; i++)
81 {
82 range[2 * i] = FLT_MAX;
83 range[2 * i + 1] = -FLT_MAX;
84 }
85 p = out;
86 for (yy = 0; yy <= FUNSEGS; yy++)
87 {
88 fv[1] = y0 + (y1 - y0) * yy / FUNSEGS;
89
90 for (xx = 0; xx <= FUNSEGS; xx++)
91 {
92 fv[0] = x0 + (x1 - x0) * xx / FUNSEGS;
93
94 if (rd->funcs == 1)
95 pdf_eval_function(ctx, func[0], fv, 2, in, n_in);
96 else
97 {
98 int zz;
99 for (zz = 0; zz < n_in; zz++)
100 pdf_eval_function(ctx, func[zz], fv, 2, &in[zz], 1);
101 }
102
103 rd->recolor(ctx, rd->opaque, rd->dst_cs, p, rd->src_cs, in);
104
105 for (i = 0; i < n_out; i++)
106 {
107 if (range[2 * i] > p[i])
108 range[2 * i] = p[i];
109 if (range[2 * i + 1] < p[i])
110 range[2 * i + 1] = p[i];
111 }
112 p += n_out;
113 }
114 }
115
116 /* Now write the function out again. */
117 fun_obj = pdf_new_dict(ctx, doc, 3);
118 pdf_dict_put_int(ctx, fun_obj, PDF_NAME(FunctionType), 0);
119
120 /* Domain */
121 obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Domain), 4);
122 pdf_array_push_real(ctx, obj, x0);
123 pdf_array_push_real(ctx, obj, x1);
124 pdf_array_push_real(ctx, obj, y0);
125 pdf_array_push_real(ctx, obj, y1);
126
127 /* Range */
128 obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Range), 4);
129 for (i = 0; i < 2*n_out; i++)
130 pdf_array_push_real(ctx, obj, range[i]);
131
132 /* Size */
133 obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Size), 2);
134 pdf_array_push_int(ctx, obj, FUNSEGS+1);
135 pdf_array_push_int(ctx, obj, FUNSEGS+1);
136
137 /* BitsPerSample */
138 pdf_dict_put_int(ctx, fun_obj, PDF_NAME(BitsPerSample), FUNBPS);
139
140 buf = fz_new_buffer(ctx, 1);
141 output = fz_new_output_with_buffer(ctx, buf);
142
143 p = out;
144 for (yy = 0; yy <= FUNSEGS; yy++)
145 {
146 for (xx = 0; xx <= FUNSEGS; xx++)
147 {
148 for (i = 0; i < n_out; i++)
149 {
150 float v = p[i];
151 float d = range[2 * i + 1] - range[2 * i];
152 int iv;
153
154 v -= range[2 * i];
155 if (d != 0)
156 v = v * ((1<<FUNBPS)-1) / d;
157 iv = (int)(v + 0.5f);
158 fz_write_bits(ctx, output, iv, FUNBPS);
159 }
160 p += n_out;
161 }
162 }
163 fz_write_bits_sync(ctx, output);
164 fz_close_output(ctx, output);
165 fz_drop_output(ctx, output);
166
167 ref = pdf_add_object(ctx, doc, fun_obj);
168 pdf_update_stream(ctx, doc, ref, buf, 0);
169 fz_drop_buffer(ctx, buf);
170 pdf_dict_put(ctx, shade, PDF_NAME(Function), ref);
171 }
172
173 static void
174 fz_recolor_shade_function(fz_context *ctx, pdf_obj *shade, float *samples, int stride, recolor_details *rd)
175 {
176 int i;
177 int n_in = fz_colorspace_n(ctx, rd->src_cs);
178 int n_out = fz_colorspace_n(ctx, rd->dst_cs);
179 float localp[256*FZ_MAX_COLORS];
180 float *q = localp;
181 float p[FZ_MAX_COLORS];
182 pdf_obj *fun_obj = NULL;
183 pdf_document *doc = pdf_get_bound_document(ctx, shade);
184 pdf_obj *obj;
185 float t0 = 0;
186 float t1 = 1;
187 float range[FZ_MAX_COLORS];
188 pdf_obj *ref = NULL;
189 fz_buffer *buf = NULL;
190 fz_output *output = NULL;
191 int t;
192
193 obj = pdf_dict_get(ctx, shade, PDF_NAME(Domain));
194 if (obj)
195 {
196 t0 = pdf_array_get_real(ctx, obj, 0);
197 t1 = pdf_array_get_real(ctx, obj, 1);
198 }
199
200 for (i = 0; i < n_out; i++)
201 {
202 range[2 * i] = FLT_MAX;
203 range[2 * i + 1] = -FLT_MAX;
204 }
205 for (t = 0; t < 256; t++)
206 {
207 for (i = 0; i < n_in; i++)
208 p[i] = samples[t*stride+i];
209
210 rd->recolor(ctx, rd->opaque, rd->dst_cs, q, rd->src_cs, p);
211
212 for (i = 0; i < n_out; i++)
213 {
214 if (range[2 * i] > q[i])
215 range[2 * i] = q[i];
216 if (range[2 * i + 1] < q[i])
217 range[2 * i + 1] = q[i];
218 }
219 q += n_out;
220 }
221
222 fz_var(ref);
223 fz_var(output);
224
225 /* Now write the function out again. */
226 fun_obj = pdf_new_dict(ctx, doc, 3);
227 fz_try(ctx)
228 {
229 pdf_dict_put_int(ctx, fun_obj, PDF_NAME(FunctionType), 0);
230
231 /* Domain */
232 obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Domain), 2);
233 pdf_array_push_real(ctx, obj, t0);
234 pdf_array_push_real(ctx, obj, t1);
235
236 /* Range */
237 obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Range), 2 * n_out);
238 for (i = 0; i < 2 * n_out; i++)
239 pdf_array_push_real(ctx, obj, range[i]);
240
241 obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Size), 1);
242 pdf_array_push_int(ctx, obj, 256);
243
244 pdf_dict_put_int(ctx, fun_obj, PDF_NAME(BitsPerSample), FUNBPS);
245
246 buf = fz_new_buffer(ctx, 1);
247 output = fz_new_output_with_buffer(ctx, buf);
248
249 q = localp;
250 for (t = 0; t < 256; t++)
251 {
252 for (i = 0; i < n_out; i++)
253 {
254 float v = q[i];
255 float d = range[2 * i + 1] - range[2 * i];
256 int iv;
257
258 v -= range[2 * i];
259 if (d != 0)
260 v = v * ((1<<FUNBPS)-1) / d;
261 iv = (int)(v + 0.5f);
262 fz_write_bits(ctx, output, iv, FUNBPS);
263 }
264 q += n_out;
265 }
266 fz_write_bits_sync(ctx, output);
267 fz_close_output(ctx, output);
268
269 ref = pdf_add_object(ctx, doc, fun_obj);
270 pdf_update_stream(ctx, doc, ref, buf, 0);
271 pdf_dict_put(ctx, shade, PDF_NAME(Function), ref);
272 }
273 fz_always(ctx)
274 {
275 fz_drop_output(ctx, output);
276 fz_drop_buffer(ctx, buf);
277 pdf_drop_obj(ctx, fun_obj);
278 pdf_drop_obj(ctx, ref);
279 }
280 fz_catch(ctx)
281 fz_rethrow(ctx);
282 }
283
284 static inline float read_sample(fz_context *ctx, fz_stream *stream, int bits, float min, float max)
285 {
286 /* we use pow(2,x) because (1<<x) would overflow the math on 32-bit samples */
287 float bitscale = 1 / (powf(2, bits) - 1);
288 return min + fz_read_bits(ctx, stream, bits) * (max - min) * bitscale;
289 }
290
291 static inline void write_sample(fz_context *ctx, fz_output *out, int bits, float min, float max, float val)
292 {
293 /* we use pow(2,x) because (1<<x) would overflow the math on 32-bit samples */
294 float bitscale = (powf(2, bits) - 1);
295 if (val < min)
296 val = min;
297 else if (val > max)
298 val = max;
299 val -= min;
300 if (max != min)
301 val /= (max - min);
302 /* Now 0 <= val <= 1 */
303 fz_write_bits(ctx, out, (int)(val * bitscale), bits);
304 }
305
306 typedef struct
307 {
308 float *p;
309 int len;
310 int max;
311 int pos;
312 } float_queue;
313
314 static void
315 float_queue_push(fz_context *ctx, float_queue *p, float f)
316 {
317 if (p->len == p->max)
318 {
319 int new_max = p->max * 2;
320 if (new_max == 0)
321 new_max = 32;
322 p->p = fz_realloc(ctx, p->p, sizeof(float) * new_max);
323 p->max = new_max;
324 }
325 p->p[p->len++] = f;
326 }
327
328 static float
329 float_queue_pop(fz_context *ctx, float_queue *p)
330 {
331 return p->p[p->pos++];
332 }
333
334 static void
335 float_queue_drop(fz_context *ctx, float_queue *p)
336 {
337 fz_free(ctx, p->p);
338 }
339
340 static void
341 read_decode(fz_context *ctx, pdf_obj *shade, int n_in, float *c_min, float *c_max, int n_out, float *d_min, float *d_max)
342 {
343 int i;
344 pdf_obj *obj = pdf_dict_get(ctx, shade, PDF_NAME(Decode));
345
346 for (i = 0; i < n_in; i++)
347 {
348 c_min[i] = pdf_array_get_int(ctx, obj, 2 * i + 4);
349 c_max[i] = pdf_array_get_int(ctx, obj, 2 * i + 5);
350 }
351 for (i = 0; i < n_out; i++)
352 {
353 d_min[i] = FLT_MAX;
354 d_max[i] = -FLT_MAX;
355 }
356 }
357
358 static void
359 rewrite_decode(fz_context *ctx, pdf_obj *shade, int n_out, float *d_min, float *d_max)
360 {
361 int i;
362 pdf_obj *obj = pdf_keep_obj(ctx, pdf_dict_get(ctx, shade, PDF_NAME(Decode)));
363 pdf_obj *obj2;
364
365 fz_try(ctx)
366 {
367 obj2 = pdf_dict_put_array(ctx, shade, PDF_NAME(Decode), 4);
368
369 for (i = 0; i < 4; i++)
370 {
371 pdf_array_push(ctx, obj2, pdf_array_get(ctx, obj, i));
372 }
373 for (i = 0; i < n_out; i++)
374 {
375 pdf_array_push_real(ctx, obj2, d_min[i]);
376 pdf_array_push_real(ctx, obj2, d_max[i]);
377 }
378 }
379 fz_always(ctx)
380 pdf_drop_obj(ctx, obj);
381 fz_catch(ctx)
382 fz_rethrow(ctx);
383 }
384
385 static void
386 fz_recolor_shade_type4(fz_context *ctx, pdf_obj *shade, recolor_details *rd)
387 {
388 fz_stream *stream;
389 int i, n_in = rd->src_cs->n;
390 int n_out = rd->dst_cs->n;
391 int bpflag = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerFlag));
392 int bpcoord = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerCoordinate));
393 int bpcomp = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerComponent));
394 pdf_document *doc = pdf_get_bound_document(ctx, shade);
395 float c[FZ_MAX_COLORS];
396 float d[FZ_MAX_COLORS];
397 float c_min[FZ_MAX_COLORS];
398 float c_max[FZ_MAX_COLORS];
399 float d_min[FZ_MAX_COLORS];
400 float d_max[FZ_MAX_COLORS];
401 fz_buffer *outbuf = NULL;
402 fz_output *out = NULL;
403 float_queue fq = { 0 };
404
405 fz_var(outbuf);
406 fz_var(out);
407 fz_var(stream);
408
409 read_decode(ctx, shade, n_in, c_min, c_max, n_out, d_min, d_max);
410
411 stream = pdf_open_stream(ctx, shade);
412 fz_try(ctx)
413 {
414 while (!fz_is_eof_bits(ctx, stream))
415 {
416 /* flag */ (void)fz_read_bits(ctx, stream, bpflag);
417 /* x_bits */ (void)fz_read_bits(ctx, stream, bpcoord);
418 /* y_bits */ (void)fz_read_bits(ctx, stream, bpcoord);
419 for (i = 0; i < n_in; i++)
420 c[i] = read_sample(ctx, stream, bpcomp, c_min[i], c_max[i]);
421
422 rd->recolor(ctx, rd->opaque, rd->dst_cs, d, rd->src_cs, c);
423
424 for (i = 0; i < n_out; i++)
425 {
426 if (d[i] < d_min[i])
427 d_min[i] = d[i];
428 if (d[i] > d_max[i])
429 d_max[i] = d[i];
430 float_queue_push(ctx, &fq, d[i]);
431 }
432 }
433 fz_drop_stream(ctx, stream);
434 stream = NULL;
435
436 rewrite_decode(ctx, shade, n_out, d_min, d_max);
437
438 stream = pdf_open_stream(ctx, shade);
439 outbuf = fz_new_buffer(ctx, 1);
440 out = fz_new_output_with_buffer(ctx, outbuf);
441 while (!fz_is_eof_bits(ctx, stream))
442 {
443 unsigned int flag = fz_read_bits(ctx, stream, bpflag);
444 unsigned int x_bits = fz_read_bits(ctx, stream, bpcoord);
445 unsigned int y_bits = fz_read_bits(ctx, stream, bpcoord);
446 for (i = 0; i < n_in; i++)
447 (void)fz_read_bits(ctx, stream, bpcomp);
448
449 fz_write_bits(ctx, out, flag, bpflag);
450 fz_write_bits(ctx, out, x_bits, bpcoord);
451 fz_write_bits(ctx, out, y_bits, bpcoord);
452
453 for (i = 0; i < n_out; i++)
454 {
455 float f = float_queue_pop(ctx, &fq);
456 write_sample(ctx, out, 8, d_min[i], d_max[i], f);
457 }
458 }
459 fz_write_bits_sync(ctx, out);
460 fz_close_output(ctx, out);
461
462 pdf_dict_put_int(ctx, shade, PDF_NAME(BitsPerComponent), 8);
463
464 pdf_update_stream(ctx, doc, shade, outbuf, 0);
465 }
466 fz_always(ctx)
467 {
468 float_queue_drop(ctx, &fq);
469 fz_drop_stream(ctx, stream);
470 fz_drop_output(ctx, out);
471 fz_drop_buffer(ctx, outbuf);
472 }
473 fz_catch(ctx)
474 {
475 fz_rethrow(ctx);
476 }
477 }
478
479 static void
480 fz_recolor_shade_type5(fz_context *ctx, pdf_obj *shade, recolor_details *rd)
481 {
482 fz_stream *stream;
483 int i, k, n_in = rd->src_cs->n;
484 int n_out = rd->dst_cs->n;
485 int bpcoord = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerCoordinate));
486 int bpcomp = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerComponent));
487 int vprow = pdf_dict_get_int(ctx, shade, PDF_NAME(VerticesPerRow));
488 pdf_document *doc = pdf_get_bound_document(ctx, shade);
489 float c[FZ_MAX_COLORS];
490 float d[FZ_MAX_COLORS];
491 float c_min[FZ_MAX_COLORS];
492 float c_max[FZ_MAX_COLORS];
493 float d_min[FZ_MAX_COLORS];
494 float d_max[FZ_MAX_COLORS];
495 fz_buffer *outbuf = NULL;
496 fz_output *out = NULL;
497 float_queue fq = { 0 };
498
499 fz_var(outbuf);
500 fz_var(out);
501 fz_var(stream);
502
503 read_decode(ctx, shade, n_in, c_min, c_max, n_out, d_min, d_max);
504
505 stream = pdf_open_stream(ctx, shade);
506 fz_try(ctx)
507 {
508 while (!fz_is_eof_bits(ctx, stream))
509 {
510 for (i = 0; i < vprow; i++)
511 {
512 /* x_bits */ (void)fz_read_bits(ctx, stream, bpcoord);
513 /* y_bits */ (void)fz_read_bits(ctx, stream, bpcoord);
514 for (k = 0; k < n_in; k++)
515 c[k] = read_sample(ctx, stream, bpcomp, c_min[k], c_max[k]);
516
517 rd->recolor(ctx, rd->opaque, rd->dst_cs, d, rd->src_cs, c);
518
519 for (k = 0; k < n_out; k++)
520 {
521 if (d[k] < d_min[k])
522 d_min[k] = d[k];
523 if (d[k] > d_max[k])
524 d_max[k] = d[k];
525 float_queue_push(ctx, &fq, d[k]);
526 }
527 }
528 }
529 fz_drop_stream(ctx, stream);
530 stream = NULL;
531
532 rewrite_decode(ctx, shade, n_out, d_min, d_max);
533
534 stream = pdf_open_stream(ctx, shade);
535 outbuf = fz_new_buffer(ctx, 1);
536 out = fz_new_output_with_buffer(ctx, outbuf);
537 while (!fz_is_eof_bits(ctx, stream))
538 {
539 for (i = 0; i < vprow; i++)
540 {
541 unsigned int x_bits = fz_read_bits(ctx, stream, bpcoord);
542 unsigned int y_bits = fz_read_bits(ctx, stream, bpcoord);
543 for (k = 0; k < n_in; k++)
544 (void)fz_read_bits(ctx, stream, bpcomp);
545
546 fz_write_bits(ctx, out, x_bits, bpcoord);
547 fz_write_bits(ctx, out, y_bits, bpcoord);
548 for (k = 0; k < n_out; k++)
549 {
550 float f = float_queue_pop(ctx, &fq);
551 write_sample(ctx, out, 8, d_min[k], d_max[k], f);
552 }
553 }
554 }
555 fz_write_bits_sync(ctx, out);
556 fz_close_output(ctx, out);
557
558 pdf_dict_put_int(ctx, shade, PDF_NAME(BitsPerComponent), 8);
559
560 pdf_update_stream(ctx, doc, shade, outbuf, 0);
561 }
562 fz_always(ctx)
563 {
564 float_queue_drop(ctx, &fq);
565 fz_drop_stream(ctx, stream);
566 fz_drop_output(ctx, out);
567 fz_drop_buffer(ctx, outbuf);
568 }
569 fz_catch(ctx)
570 {
571 fz_rethrow(ctx);
572 }
573 }
574
575 static void
576 fz_recolor_shade_type6(fz_context *ctx, pdf_obj *shade, recolor_details *rd)
577 {
578 fz_stream *stream;
579 int i, k, n_in = rd->src_cs->n;
580 int n_out = rd->dst_cs->n;
581 int bpflag = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerFlag));
582 int bpcoord = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerCoordinate));
583 int bpcomp = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerComponent));
584 pdf_document *doc = pdf_get_bound_document(ctx, shade);
585 float c[FZ_MAX_COLORS];
586 float d[FZ_MAX_COLORS];
587 float c_min[FZ_MAX_COLORS];
588 float c_max[FZ_MAX_COLORS];
589 float d_min[FZ_MAX_COLORS];
590 float d_max[FZ_MAX_COLORS];
591 fz_buffer *outbuf = NULL;
592 fz_output *out = NULL;
593 float_queue fq = { 0 };
594
595 fz_var(outbuf);
596 fz_var(out);
597 fz_var(stream);
598
599 read_decode(ctx, shade, n_in, c_min, c_max, n_out, d_min, d_max);
600
601 stream = pdf_open_stream(ctx, shade);
602 fz_try(ctx)
603 {
604 while (!fz_is_eof_bits(ctx, stream))
605 {
606 int startcolor;
607 int startpt;
608
609 int flag = fz_read_bits(ctx, stream, bpflag);
610
611 if (flag == 0)
612 {
613 startpt = 0;
614 startcolor = 0;
615 }
616 else
617 {
618 startpt = 4;
619 startcolor = 2;
620 }
621
622 for (i = startpt; i < 12; i++)
623 {
624 unsigned int x_bits = fz_read_bits(ctx, stream, bpcoord);
625 unsigned int y_bits = fz_read_bits(ctx, stream, bpcoord);
626 fz_write_bits(ctx, out, x_bits, bpcoord);
627 fz_write_bits(ctx, out, y_bits, bpcoord);
628 }
629
630 for (i = startcolor; i < 4; i++)
631 {
632 for (k = 0; k < n_in; k++)
633 c[k] = read_sample(ctx, stream, bpcomp, c_min[k], c_max[k]);
634
635 rd->recolor(ctx, rd->opaque, rd->dst_cs, d, rd->src_cs, c);
636
637 for (k = 0; k < n_out; k++)
638 {
639 if (d[k] < d_min[k])
640 d_min[k] = d[k];
641 if (d[k] > d_max[k])
642 d_max[k] = d[k];
643 float_queue_push(ctx, &fq, d[k]);
644 }
645 }
646 }
647 fz_drop_stream(ctx, stream);
648 stream = NULL;
649
650 rewrite_decode(ctx, shade, n_out, d_min, d_max);
651
652 stream = pdf_open_stream(ctx, shade);
653 outbuf = fz_new_buffer(ctx, 1);
654 out = fz_new_output_with_buffer(ctx, outbuf);
655 while (!fz_is_eof_bits(ctx, stream))
656 {
657 int startcolor;
658 int startpt;
659
660 int flag = fz_read_bits(ctx, stream, bpflag);
661
662 fz_write_bits(ctx, out, flag, bpflag);
663
664 if (flag == 0)
665 {
666 startpt = 0;
667 startcolor = 0;
668 }
669 else
670 {
671 startpt = 4;
672 startcolor = 2;
673 }
674
675 for (i = startpt; i < 12; i++)
676 {
677 unsigned int x_bits = fz_read_bits(ctx, stream, bpcoord);
678 unsigned int y_bits = fz_read_bits(ctx, stream, bpcoord);
679 fz_write_bits(ctx, out, x_bits, bpcoord);
680 fz_write_bits(ctx, out, y_bits, bpcoord);
681 }
682
683 for (i = startcolor; i < 4; i++)
684 {
685 for (k = 0; k < n_in; k++)
686 (void)fz_read_bits(ctx, stream, bpcomp);
687
688 for (k = 0; k < n_out; k++)
689 {
690 float f = float_queue_pop(ctx, &fq);
691 write_sample(ctx, out, 8, d_min[k], d_max[k], f);
692 }
693 }
694 }
695 fz_write_bits_sync(ctx, out);
696 fz_close_output(ctx, out);
697
698 pdf_dict_put_int(ctx, shade, PDF_NAME(BitsPerComponent), 8);
699
700 pdf_update_stream(ctx, doc, shade, outbuf, 0);
701 }
702 fz_always(ctx)
703 {
704 float_queue_drop(ctx, &fq);
705 fz_drop_stream(ctx, stream);
706 fz_drop_output(ctx, out);
707 fz_drop_buffer(ctx, outbuf);
708 }
709 fz_catch(ctx)
710 {
711 fz_rethrow(ctx);
712 }
713 }
714
715 static void
716 fz_recolor_shade_type7(fz_context *ctx, pdf_obj *shade, recolor_details *rd)
717 {
718 fz_stream *stream;
719 int i, k, n_in = rd->src_cs->n;
720 int n_out = rd->dst_cs->n;
721 int bpflag = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerFlag));
722 int bpcoord = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerCoordinate));
723 int bpcomp = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerComponent));
724 pdf_document *doc = pdf_get_bound_document(ctx, shade);
725 float c[FZ_MAX_COLORS];
726 float d[FZ_MAX_COLORS];
727 float c_min[FZ_MAX_COLORS];
728 float c_max[FZ_MAX_COLORS];
729 float d_min[FZ_MAX_COLORS];
730 float d_max[FZ_MAX_COLORS];
731 fz_buffer *outbuf = NULL;
732 fz_output *out = NULL;
733 float_queue fq = { 0 };
734
735 fz_var(outbuf);
736 fz_var(out);
737 fz_var(stream);
738
739 read_decode(ctx, shade, n_in, c_min, c_max, n_out, d_min, d_max);
740
741 stream = pdf_open_stream(ctx, shade);
742 fz_try(ctx)
743 {
744 while (!fz_is_eof_bits(ctx, stream))
745 {
746 int startcolor;
747 int startpt;
748
749 int flag = fz_read_bits(ctx, stream, bpflag);
750
751 if (flag == 0)
752 {
753 startpt = 0;
754 startcolor = 0;
755 }
756 else
757 {
758 startpt = 4;
759 startcolor = 2;
760 }
761
762 for (i = startpt; i < 16; i++)
763 {
764 /* x_bits */ (void)fz_read_bits(ctx, stream, bpcoord);
765 /* y_bits */ (void)fz_read_bits(ctx, stream, bpcoord);
766 }
767
768 for (i = startcolor; i < 4; i++)
769 {
770 for (k = 0; k < n_in; k++)
771 c[k] = read_sample(ctx, stream, bpcomp, c_min[k], c_max[k]);
772
773 rd->recolor(ctx, rd->opaque, rd->dst_cs, d, rd->src_cs, c);
774
775 for (k = 0; k < n_out; k++)
776 {
777 if (d[k] < d_min[k])
778 d_min[k] = d[k];
779 if (d[k] > d_max[k])
780 d_max[k] = d[k];
781 float_queue_push(ctx, &fq, d[k]);
782 }
783 }
784 }
785 fz_drop_stream(ctx, stream);
786 stream = NULL;
787
788 rewrite_decode(ctx, shade, n_out, d_min, d_max);
789
790 stream = pdf_open_stream(ctx, shade);
791 outbuf = fz_new_buffer(ctx, 1);
792 out = fz_new_output_with_buffer(ctx, outbuf);
793 while (!fz_is_eof_bits(ctx, stream))
794 {
795 int startcolor;
796 int startpt;
797
798 int flag = fz_read_bits(ctx, stream, bpflag);
799
800 fz_write_bits(ctx, out, flag, bpflag);
801
802 if (flag == 0)
803 {
804 startpt = 0;
805 startcolor = 0;
806 }
807 else
808 {
809 startpt = 4;
810 startcolor = 2;
811 }
812
813 for (i = startpt; i < 16; i++)
814 {
815 unsigned int x_bits = fz_read_bits(ctx, stream, bpcoord);
816 unsigned int y_bits = fz_read_bits(ctx, stream, bpcoord);
817 fz_write_bits(ctx, out, x_bits, bpcoord);
818 fz_write_bits(ctx, out, y_bits, bpcoord);
819 }
820
821 for (i = startcolor; i < 4; i++)
822 {
823 for (k = 0; k < n_in; k++)
824 (void)fz_read_bits(ctx, stream, bpcomp);
825
826 for (k = 0; k < n_out; k++)
827 {
828 float f = float_queue_pop(ctx, &fq);
829 write_sample(ctx, out, 8, d_min[k], d_max[k], f);
830 }
831 }
832
833 }
834 fz_write_bits_sync(ctx, out);
835 fz_close_output(ctx, out);
836
837 pdf_dict_put_int(ctx, shade, PDF_NAME(BitsPerComponent), 8);
838
839 pdf_update_stream(ctx, doc, shade, outbuf, 0);
840 }
841 fz_always(ctx)
842 {
843 float_queue_drop(ctx, &fq);
844 fz_drop_stream(ctx, stream);
845 fz_drop_output(ctx, out);
846 fz_drop_buffer(ctx, outbuf);
847 }
848 fz_catch(ctx)
849 {
850 fz_rethrow(ctx);
851 }
852 }
853
854 pdf_obj *
855 pdf_new_colorspace(fz_context *ctx, fz_colorspace *cs)
856 {
857 switch (fz_colorspace_type(ctx, cs))
858 {
859 case FZ_COLORSPACE_GRAY:
860 return PDF_NAME(DeviceGray);
861 case FZ_COLORSPACE_RGB:
862 return PDF_NAME(DeviceRGB);
863 case FZ_COLORSPACE_CMYK:
864 return PDF_NAME(DeviceCMYK);
865 default:
866 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unimplemented colorspace");
867 }
868 }
869
870 pdf_obj *
871 pdf_recolor_shade(fz_context *ctx, pdf_obj *shade, pdf_shade_recolorer *reshade, void *opaque)
872 {
873 recolor_details rd;
874 fz_colorspace *src_cs;
875 pdf_obj *background, *new_bg = NULL;
876 pdf_obj *function;
877 pdf_obj *rewritten = NULL;
878 pdf_obj *obj;
879 int type, i;
880 pdf_function *func[FZ_MAX_COLORS] = { NULL };
881 float d0, d1;
882 float samples[256*(FZ_MAX_COLORS + 1)];
883 pdf_document *doc = pdf_get_bound_document(ctx, shade);
884
885 src_cs = pdf_load_colorspace(ctx, pdf_dict_get(ctx, shade, PDF_NAME(ColorSpace)));
886
887 fz_var(rewritten);
888
889 rd.funcs = 0;
890
891 fz_try(ctx)
892 {
893 rd.recolor = reshade(ctx, opaque, src_cs, &rd.dst_cs);
894 if (rd.recolor == NULL)
895 break;
896
897 rd.src_cs = src_cs;
898 rd.opaque = opaque;
899
900 rewritten = pdf_deep_copy_obj(ctx, shade);
901
902 type = pdf_dict_get_int(ctx, shade, PDF_NAME(ShadingType));
903
904 pdf_dict_put_drop(ctx, rewritten, PDF_NAME(ColorSpace), pdf_new_colorspace(ctx, rd.dst_cs));
905
906 background = pdf_dict_get(ctx, shade, PDF_NAME(Background));
907 if (background)
908 {
909 int n = pdf_array_len(ctx, background);
910 float bg[FZ_MAX_COLORS];
911 float nbg[FZ_MAX_COLORS];
912
913 if (n > FZ_MAX_COLORS)
914 fz_throw(ctx, FZ_ERROR_SYNTAX, "Too many background components");
915 if (n != src_cs->n)
916 fz_throw(ctx, FZ_ERROR_SYNTAX, "Wrong background dimension");
917
918 for (i = 0; i < n; i++)
919 bg[i] = pdf_array_get_real(ctx, background, i);
920
921 rd.recolor(ctx, rd.opaque, rd.dst_cs, nbg, src_cs, bg);
922
923 new_bg = pdf_dict_put_array(ctx, rewritten, PDF_NAME(Background), rd.dst_cs->n);
924 for (i = 0; i < n; i++)
925 pdf_array_put_real(ctx, new_bg, i, bg[i]);
926 pdf_dict_put(ctx, rewritten, PDF_NAME(Background), new_bg);
927 }
928
929 d0 = 0;
930 d1 = 1;
931 obj = pdf_dict_get(ctx, shade, PDF_NAME(Domain));
932 if (obj)
933 {
934 d0 = pdf_array_get_real(ctx, obj, 0);
935 d1 = pdf_array_get_real(ctx, obj, 1);
936 }
937
938 function = pdf_dict_get(ctx, shade, PDF_NAME(Function));
939 if (pdf_is_dict(ctx, function))
940 {
941 rd.funcs = 1;
942 func[0] = pdf_load_function(ctx, function, type == 1 ? 2 : 1, src_cs->n);
943 if (!func[0])
944 fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot load shading function (%d 0 R)", pdf_to_num(ctx, obj));
945
946 if (type != 1)
947 pdf_sample_shade_function(ctx, samples, src_cs->n, 1, func, d0, d1);
948 }
949 else if (pdf_is_array(ctx, function))
950 {
951 int in;
952
953 rd.funcs = pdf_array_len(ctx, function);
954
955 if (rd.funcs != 1 && rd.funcs != src_cs->n)
956 fz_throw(ctx, FZ_ERROR_SYNTAX, "incorrect number of shading functions");
957 if (rd.funcs > FZ_MAX_COLORS)
958 fz_throw(ctx, FZ_ERROR_SYNTAX, "too many shading functions");
959 if (type == 1)
960 in = 2;
961 else
962 in = 1;
963
964 for (i = 0; i < rd.funcs; i++)
965 {
966 func[i] = pdf_load_function(ctx, pdf_array_get(ctx, function, i), in, 1);
967 if (!func[i])
968 fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot load shading function (%d 0 R)", pdf_to_num(ctx, obj));
969 }
970
971 if (type != 1)
972 pdf_sample_shade_function(ctx, samples, src_cs->n, rd.funcs, func, d0, d1);
973 }
974 else if (type < 4)
975 {
976 /* Functions are compulsory for types 1,2,3 */
977 fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot load shading function (%d 0 R)", pdf_to_num(ctx, obj));
978 }
979
980 /* For function based shadings, we rewrite the 2d function. */
981 if (type == 1)
982 {
983 fz_recolor_shade_type1(ctx, rewritten, func, &rd);
984 break;
985 }
986
987 /* For all other function based shadings, we just rewrite the 1d function. */
988 if (rd.funcs)
989 {
990 fz_recolor_shade_function(ctx, rewritten, samples, src_cs->n+1, &rd);
991 break;
992 }
993
994 /* From here on in, we're changing the mesh, which means altering a stream.
995 * We'll need to be an indirect for that to work. */
996 obj = pdf_add_object(ctx, doc, rewritten);
997 pdf_drop_obj(ctx, rewritten);
998 rewritten = obj;
999
1000 switch (type)
1001 {
1002 case FZ_FUNCTION_BASED:
1003 /* Can never reach here. */
1004 break;
1005 case FZ_LINEAR:
1006 case FZ_RADIAL:
1007 fz_throw(ctx, FZ_ERROR_SYNTAX, "Linear/Radial shadings must use functions");
1008 break;
1009 case FZ_MESH_TYPE4:
1010 fz_recolor_shade_type4(ctx, rewritten, &rd);
1011 break;
1012 case FZ_MESH_TYPE5:
1013 fz_recolor_shade_type5(ctx, rewritten, &rd);
1014 break;
1015 case FZ_MESH_TYPE6:
1016 fz_recolor_shade_type6(ctx, rewritten, &rd);
1017 break;
1018 case FZ_MESH_TYPE7:
1019 fz_recolor_shade_type7(ctx, rewritten, &rd);
1020 break;
1021 default:
1022 fz_throw(ctx, FZ_ERROR_SYNTAX, "Unexpected mesh type %d\n", type);
1023 }
1024 }
1025 fz_always(ctx)
1026 {
1027 for (i = 0; i < rd.funcs; i++)
1028 pdf_drop_function(ctx, func[i]);
1029 fz_drop_colorspace(ctx, src_cs);
1030 }
1031 fz_catch(ctx)
1032 {
1033 pdf_drop_obj(ctx, rewritten);
1034 fz_rethrow(ctx);
1035 }
1036
1037 return rewritten ? rewritten : shade;
1038 }