comparison mupdf-source/source/fitz/draw-mesh.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
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2021 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
25 #include "color-imp.h"
26 #include "draw-imp.h"
27 #include "pixmap-imp.h"
28
29 #include <assert.h>
30 #include <math.h>
31
32 enum { MAXN = 2 + FZ_MAX_COLORS };
33
34 static void paint_scan(fz_pixmap *FZ_RESTRICT pix, int y, int fx0, int fx1, int cx0, int cx1, const int *FZ_RESTRICT v0, const int *FZ_RESTRICT v1, int n)
35 {
36 unsigned char *p;
37 int c[MAXN], dc[MAXN];
38 int k, w;
39 float div, mul;
40 int x0, x1, pa;
41
42 /* Ensure that fx0 is left edge, and fx1 is right */
43 if (fx0 > fx1)
44 {
45 const int *v;
46 int t = fx0; fx0 = fx1; fx1 = t;
47 v = v0; v0 = v1; v1 = v;
48 }
49 else if (fx0 == fx1)
50 return;
51
52 /* Clip fx0, fx1 to range */
53 if (fx0 >= cx1)
54 return;
55 if (fx1 <= cx0)
56 return;
57 x0 = (fx0 > cx0 ? fx0 : cx0);
58 x1 = (fx1 < cx1 ? fx1 : cx1);
59
60 w = x1 - x0;
61 if (w == 0)
62 return;
63
64 div = 1.0f / (fx1 - fx0);
65 mul = (x0 - fx0);
66 for (k = 0; k < n; k++)
67 {
68 dc[k] = (v1[k] - v0[k]) * div;
69 c[k] = v0[k] + dc[k] * mul;
70 }
71
72 p = pix->samples + ((x0 - pix->x) * pix->n) + ((y - pix->y) * pix->stride);
73 pa = pix->alpha;
74 do
75 {
76 for (k = 0; k < n; k++)
77 {
78 *p++ = c[k]>>16;
79 c[k] += dc[k];
80 }
81 if (pa)
82 *p++ = 255;
83 }
84 while (--w);
85 }
86
87 typedef struct
88 {
89 float x;
90 float dx;
91 int v[2*MAXN];
92 } edge_data;
93
94 static inline void prepare_edge(const float *FZ_RESTRICT vtop, const float *FZ_RESTRICT vbot, edge_data *FZ_RESTRICT edge, float y, int n)
95 {
96 float r = 1.0f / (vbot[1] - vtop[1]);
97 float t = (y - vtop[1]) * r;
98 float diff = vbot[0] - vtop[0];
99 int i;
100
101 edge->x = vtop[0] + diff * t;
102 edge->dx = diff * r;
103
104 for (i = 0; i < n; i++)
105 {
106 diff = vbot[i+2] - vtop[i+2];
107 edge->v[i] = (int)(65536.0f * (vtop[i+2] + diff * t));
108 edge->v[i+MAXN] = (int)(65536.0f * diff * r);
109 }
110 }
111
112 static inline void step_edge(edge_data *edge, int n)
113 {
114 int i;
115
116 edge->x += edge->dx;
117
118 for (i = 0; i < n; i++)
119 {
120 edge->v[i] += edge->v[i + MAXN];
121 }
122 }
123
124 static void
125 fz_paint_triangle(fz_pixmap *pix, float *v[3], int n, fz_irect bbox)
126 {
127 edge_data e0, e1;
128 int top, mid, bot;
129 float y, y1;
130 int minx, maxx;
131
132 top = bot = 0;
133 if (v[1][1] < v[0][1]) top = 1; else bot = 1;
134 if (v[2][1] < v[top][1]) top = 2;
135 else if (v[2][1] > v[bot][1]) bot = 2;
136 if (v[top][1] == v[bot][1]) return;
137
138 /* Test if the triangle is completely outside the scissor rect */
139 if (v[bot][1] < bbox.y0) return;
140 if (v[top][1] > bbox.y1) return;
141
142 /* Magic! Ensure that mid/top/bot are all different */
143 mid = 3^top^bot;
144
145 assert(top != bot && top != mid && mid != bot);
146
147 minx = fz_maxi(bbox.x0, pix->x);
148 maxx = fz_mini(bbox.x1, pix->x + pix->w);
149
150 y = ceilf(fz_max(bbox.y0, v[top][1]));
151 y1 = ceilf(fz_min(bbox.y1, v[mid][1]));
152
153 n -= 2;
154 prepare_edge(v[top], v[bot], &e0, y, n);
155 if (y < y1)
156 {
157 prepare_edge(v[top], v[mid], &e1, y, n);
158
159 do
160 {
161 paint_scan(pix, y, (int)e0.x, (int)e1.x, minx, maxx, &e0.v[0], &e1.v[0], n);
162 step_edge(&e0, n);
163 step_edge(&e1, n);
164 y ++;
165 }
166 while (y < y1);
167 }
168
169 y1 = ceilf(fz_min(bbox.y1, v[bot][1]));
170 if (y < y1)
171 {
172 prepare_edge(v[mid], v[bot], &e1, y, n);
173
174 do
175 {
176 paint_scan(pix, y, (int)e0.x, (int)e1.x, minx, maxx, &e0.v[0], &e1.v[0], n);
177 y ++;
178 if (y >= y1)
179 break;
180 step_edge(&e0, n);
181 step_edge(&e1, n);
182 }
183 while (1);
184 }
185 }
186
187 struct paint_tri_data
188 {
189 const fz_shade *shade;
190 fz_pixmap *dest;
191 fz_irect bbox;
192 fz_color_converter cc;
193 };
194
195 static void
196 prepare_mesh_vertex(fz_context *ctx, void *arg, fz_vertex *v, const float *input)
197 {
198 struct paint_tri_data *ptd = (struct paint_tri_data *)arg;
199 const fz_shade *shade = ptd->shade;
200 fz_pixmap *dest = ptd->dest;
201 float *output = v->c;
202 int i;
203
204 if (shade->function_stride)
205 {
206 float f = input[0];
207 if (shade->type >= 4 && shade->type <= 7)
208 f = (f - shade->u.m.c0[0]) / (shade->u.m.c1[0] - shade->u.m.c0[0]);
209 output[0] = f * 255;
210 }
211 else
212 {
213 int n = fz_colorspace_n(ctx, dest->colorspace);
214 int a = dest->alpha;
215 int m = dest->n - a;
216 for (i = n; i < m; i++)
217 output[i] = 0;
218 if (ptd->cc.convert)
219 ptd->cc.convert(ctx, &ptd->cc, input, output);
220 for (i = 0; i < m; i++)
221 output[i] *= 255;
222 if (a)
223 output[m] = 255;
224 }
225 }
226
227 static void
228 do_paint_tri(fz_context *ctx, void *arg, fz_vertex *av, fz_vertex *bv, fz_vertex *cv)
229 {
230 struct paint_tri_data *ptd = (struct paint_tri_data *)arg;
231 float *vertices[3];
232 fz_pixmap *dest;
233
234 vertices[0] = (float *)av;
235 vertices[1] = (float *)bv;
236 vertices[2] = (float *)cv;
237
238 dest = ptd->dest;
239 fz_paint_triangle(dest, vertices, 2 + dest->n - dest->alpha, ptd->bbox);
240 }
241
242 struct fz_shade_color_cache
243 {
244 fz_colorspace *src;
245 fz_colorspace *dst;
246 fz_color_params params;
247 int full;
248 fz_color_converter cached;
249 fz_colorspace *src2;
250 fz_colorspace *dst2;
251 fz_color_params params2;
252 int full2;
253 fz_color_converter cached2;
254 };
255
256 void
257 fz_drop_shade_color_cache(fz_context *ctx, fz_shade_color_cache *cache)
258 {
259 if (cache == NULL)
260 return;
261
262 fz_drop_colorspace(ctx, cache->src);
263 fz_drop_colorspace(ctx, cache->dst);
264 if (cache->full)
265 fz_fin_cached_color_converter(ctx, &cache->cached);
266
267 fz_drop_colorspace(ctx, cache->src2);
268 fz_drop_colorspace(ctx, cache->dst2);
269 if (cache->full2)
270 fz_drop_color_converter(ctx, &cache->cached2);
271
272 fz_free(ctx, cache);
273 }
274
275 void
276 fz_paint_shade(fz_context *ctx, fz_shade *shade, fz_colorspace *colorspace, fz_matrix ctm, fz_pixmap *dest, fz_color_params color_params, fz_irect bbox, const fz_overprint *eop, fz_shade_color_cache **color_cache)
277 {
278 unsigned char clut[256][FZ_MAX_COLORS];
279 fz_pixmap *temp = NULL;
280 fz_pixmap *conv = NULL;
281 fz_color_converter cc = { 0 };
282 float color[FZ_MAX_COLORS];
283 struct paint_tri_data ptd = { 0 };
284 int i, k;
285 fz_matrix local_ctm;
286 fz_shade_color_cache *cache = NULL;
287 int recache = 0;
288 int recache2 = 0;
289 int stride = shade->function_stride;
290
291 fz_var(temp);
292 fz_var(conv);
293 fz_var(recache);
294 fz_var(recache2);
295 fz_var(cc);
296
297 if (colorspace == NULL)
298 colorspace = shade->colorspace;
299
300 if (color_cache)
301 {
302 cache = *color_cache;
303 if (cache == NULL)
304 *color_cache = cache = fz_malloc_struct(ctx, fz_shade_color_cache);
305 }
306
307 fz_try(ctx)
308 {
309 local_ctm = fz_concat(shade->matrix, ctm);
310
311 if (stride)
312 {
313 /* We need to use alpha = 1 here, because the shade might not fill the bbox. */
314 temp = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), bbox, NULL, 1);
315 fz_clear_pixmap(ctx, temp);
316 }
317 else
318 {
319 temp = dest;
320 }
321
322 ptd.dest = temp;
323 ptd.shade = shade;
324 ptd.bbox = bbox;
325
326 if (temp->colorspace)
327 {
328 if (cache && cache->full && cache->src == colorspace && cache->dst == temp->colorspace &&
329 cache->params.op == color_params.op &&
330 cache->params.opm == color_params.opm &&
331 cache->params.ri == color_params.ri)
332 {
333 ptd.cc = cache->cached;
334 cache->full = 0;
335 }
336 else
337 fz_init_cached_color_converter(ctx, &ptd.cc, colorspace, temp->colorspace, temp->seps, NULL, color_params);
338
339 /* Drop the existing contents of the cache. */
340 if (cache)
341 {
342 fz_drop_colorspace(ctx, cache->src);
343 cache->src = NULL;
344 fz_drop_colorspace(ctx, cache->dst);
345 cache->dst = NULL;
346 if (cache->full)
347 fz_fin_cached_color_converter(ctx, &cache->cached);
348 cache->full = 0;
349
350 /* Remember that we can put stuff back into the cache. */
351 recache = 1;
352 }
353 }
354
355 fz_process_shade(ctx, shade, local_ctm, fz_rect_from_irect(bbox), prepare_mesh_vertex, &do_paint_tri, &ptd);
356
357 if (stride)
358 {
359 /* If the shade is defined in a deviceN (or separation,
360 * which is the same internally to MuPDF) space, then
361 * we need to render it in deviceN before painting it
362 * to the destination. If not, we are free to render it
363 * direct to the target. */
364 if (fz_colorspace_is_device_n(ctx, colorspace))
365 {
366 /* We've drawn it as greyscale, with the values being
367 * the input to the function. Now make DevN version
368 * by mapping that greyscale through the function.
369 * This seems inefficient, but it's actually required,
370 * because we need to apply the function lookup POST
371 * interpolation in the do_paint_tri routines, not
372 * before it to avoid problems with some test files
373 * (tests/GhentV3.0/061_Shading_x1a.pdf for example).
374 */
375 unsigned char *s = temp->samples;
376 unsigned char *d;
377 int hh = temp->h;
378 int n = fz_colorspace_n(ctx, colorspace);
379
380 /* alpha = 1 here for the same reason as earlier */
381 conv = fz_new_pixmap_with_bbox(ctx, colorspace, bbox, NULL, 1);
382 d = conv->samples;
383 while (hh--)
384 {
385 int len = temp->w;
386 while (len--)
387 {
388 int v = *s++;
389 int a = *s++;
390 const float *f = &shade->function[v*stride];
391 for (k = 0; k < n; k++)
392 *d++ = fz_clampi(255 * f[k], 0, 255);
393 *d++ = a;
394 }
395 d += conv->stride - conv->w * (size_t)conv->n;
396 s += temp->stride - temp->w * (size_t)temp->n;
397 }
398 fz_drop_pixmap(ctx, temp);
399 temp = conv;
400 conv = NULL;
401
402 /* Now Change from our device_n colorspace into the target colorspace/spots. */
403 conv = fz_clone_pixmap_area_with_different_seps(ctx, temp, NULL, dest->colorspace, dest->seps, color_params, NULL);
404 }
405 else
406 {
407 unsigned char *s = temp->samples;
408 unsigned char *d;
409 int da;
410 int sa = temp->alpha;
411 int hh = temp->h;
412 int cn = fz_colorspace_n(ctx, colorspace);
413 int m = dest->n - dest->alpha;
414 int n = fz_colorspace_n(ctx, dest->colorspace);
415
416 if (dest->colorspace)
417 {
418 if (cache && cache->full2 && cache->src2 == colorspace && cache->dst2 == dest->colorspace &&
419 cache->params2.op == color_params.op &&
420 cache->params2.opm == color_params.opm &&
421 cache->params2.ri == color_params.ri)
422 {
423 cc = cache->cached2;
424 cache->full2 = 0;
425 }
426 else
427 fz_find_color_converter(ctx, &cc, colorspace, dest->colorspace, dest->seps, NULL, color_params);
428
429 /* Drop the existing contents of the cache */
430 if (cache)
431 {
432 fz_drop_colorspace(ctx, cache->src2);
433 cache->src2 = NULL;
434 fz_drop_colorspace(ctx, cache->dst2);
435 cache->dst2 = NULL;
436 if (cache->full2)
437 fz_drop_color_converter(ctx, &cache->cached2);
438 cache->full2 = 0;
439
440 /* Remember that we can put stuff back into the cache. */
441 recache2 = 1;
442 }
443 for (i = 0; i < 256; i++)
444 {
445 cc.convert(ctx, &cc, &shade->function[i*stride], color);
446 for (k = 0; k < n; k++)
447 clut[i][k] = color[k] * 255;
448 for (; k < m; k++)
449 clut[i][k] = 0;
450 clut[i][k] = shade->function[i*stride + cn] * 255;
451 }
452 }
453 else
454 {
455 for (i = 0; i < 256; i++)
456 {
457 for (k = 0; k < m; k++)
458 clut[i][k] = 0;
459 clut[i][k] = shade->function[i*stride + cn] * 255;
460 }
461 }
462
463 conv = fz_new_pixmap_with_bbox(ctx, dest->colorspace, bbox, dest->seps, 1);
464 d = conv->samples;
465 da = conv->alpha;
466 while (hh--)
467 {
468 int len = temp->w;
469 while (len--)
470 {
471 int v = *s++;
472 int a = (da ? clut[v][conv->n - 1] : 255);
473 if (sa)
474 a = fz_mul255(*s++, a);
475 for (k = 0; k < conv->n - da; k++)
476 *d++ = fz_mul255(clut[v][k], a);
477 if (da)
478 *d++ = a;
479 }
480 d += conv->stride - conv->w * (size_t)conv->n;
481 s += temp->stride - temp->w * (size_t)temp->n;
482 }
483 }
484 fz_paint_pixmap_with_overprint(dest, conv, eop);
485 }
486 }
487 fz_always(ctx)
488 {
489 if (recache)
490 {
491 cache->src = fz_keep_colorspace(ctx, colorspace);
492 cache->dst = fz_keep_colorspace(ctx, temp->colorspace);
493 cache->params = color_params;
494 cache->cached = ptd.cc;
495 cache->full = 1;
496 }
497 else
498 fz_fin_cached_color_converter(ctx, &ptd.cc);
499 if (stride)
500 {
501 if (recache2)
502 {
503 cache->src2 = fz_keep_colorspace(ctx, colorspace);
504 cache->dst2 = fz_keep_colorspace(ctx, dest->colorspace);
505 cache->params2 = color_params;
506 cache->cached2 = cc;
507 cache->full2 = 1;
508 }
509 else
510 fz_drop_color_converter(ctx, &cc);
511 fz_drop_pixmap(ctx, temp);
512 fz_drop_pixmap(ctx, conv);
513 }
514 }
515 fz_catch(ctx)
516 fz_rethrow(ctx);
517 }