Coverage Report

Created: 2024-05-20 06:23

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