Coverage Report

Created: 2025-01-28 06:17

/src/mupdf/source/fitz/draw-edge.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
#include "draw-imp.h"
25
26
#include <assert.h>
27
#include <limits.h>
28
#include <math.h>
29
#include <stdlib.h>
30
#include <string.h>
31
32
/*
33
 * Global Edge List -- list of straight path segments for scan conversion
34
 *
35
 * Stepping along the edges is with Bresenham's line algorithm.
36
 *
37
 * See Mike Abrash -- Graphics Programming Black Book (notably chapter 40)
38
 */
39
40
typedef struct fz_edge_s
41
{
42
  int x, e, h, y;
43
  int adj_up, adj_down;
44
  int xmove;
45
  int xdir, ydir; /* -1 or +1 */
46
} fz_edge;
47
48
typedef struct fz_gel_s
49
{
50
  fz_rasterizer super;
51
  int cap, len;
52
  fz_edge *edges;
53
  int acap, alen;
54
  fz_edge **active;
55
  int bcap;
56
  unsigned char *alphas;
57
  int *deltas;
58
} fz_gel;
59
60
static int
61
fz_reset_gel(fz_context *ctx, fz_rasterizer *rast)
62
685k
{
63
685k
  fz_gel *gel = (fz_gel *)rast;
64
65
685k
  gel->len = 0;
66
685k
  gel->alen = 0;
67
68
685k
  return 0;
69
685k
}
70
71
static void
72
fz_drop_gel(fz_context *ctx, fz_rasterizer *rast)
73
16.9k
{
74
16.9k
  fz_gel *gel = (fz_gel *)rast;
75
16.9k
  if (gel == NULL)
76
0
    return;
77
16.9k
  fz_free(ctx, gel->active);
78
16.9k
  fz_free(ctx, gel->edges);
79
16.9k
  fz_free(ctx, gel->alphas);
80
16.9k
  fz_free(ctx, gel->deltas);
81
16.9k
  fz_free(ctx, gel);
82
16.9k
}
83
84
enum { INSIDE, OUTSIDE, LEAVE, ENTER };
85
86
233M
#define clip_lerp_y(v,m,x0,y0,x1,y1,t) clip_lerp_x(v,m,y0,x0,y1,x1,t)
87
88
static int
89
clip_lerp_x(int val, int m, int x0, int y0, int x1, int y1, int *out)
90
296M
{
91
296M
  int v0out = m ? x0 > val : x0 < val;
92
296M
  int v1out = m ? x1 > val : x1 < val;
93
94
296M
  if (v0out + v1out == 0)
95
136M
    return INSIDE;
96
97
160M
  if (v0out + v1out == 2)
98
159M
    return OUTSIDE;
99
100
469k
  if (v1out)
101
234k
  {
102
234k
    *out = y0 + (int)(((float)(y1 - y0)) * (val - x0) / (x1 - x0));
103
234k
    return LEAVE;
104
234k
  }
105
106
234k
  else
107
234k
  {
108
234k
    *out = y1 + (int)(((float)(y0 - y1)) * (val - x1) / (x0 - x1));
109
234k
    return ENTER;
110
234k
  }
111
469k
}
112
113
static void
114
fz_insert_gel_raw(fz_context *ctx, fz_rasterizer *ras, int x0, int y0, int x1, int y1)
115
32.8M
{
116
32.8M
  fz_gel *gel = (fz_gel *)ras;
117
32.8M
  fz_edge *edge;
118
32.8M
  int dx, dy;
119
32.8M
  int winding;
120
32.8M
  int width;
121
32.8M
  int tmp;
122
123
32.8M
  if (y0 == y1)
124
18.3M
    return;
125
126
14.4M
  if (y0 > y1) {
127
7.23M
    winding = -1;
128
7.23M
    tmp = x0; x0 = x1; x1 = tmp;
129
7.23M
    tmp = y0; y0 = y1; y1 = tmp;
130
7.23M
  }
131
7.23M
  else
132
7.23M
    winding = 1;
133
134
14.4M
  if (x0 < gel->super.bbox.x0) gel->super.bbox.x0 = x0;
135
14.4M
  if (x0 > gel->super.bbox.x1) gel->super.bbox.x1 = x0;
136
14.4M
  if (x1 < gel->super.bbox.x0) gel->super.bbox.x0 = x1;
137
14.4M
  if (x1 > gel->super.bbox.x1) gel->super.bbox.x1 = x1;
138
139
14.4M
  if (y0 < gel->super.bbox.y0) gel->super.bbox.y0 = y0;
140
14.4M
  if (y1 > gel->super.bbox.y1) gel->super.bbox.y1 = y1;
141
142
14.4M
  if (gel->len + 1 == gel->cap) {
143
1.43k
    int new_cap = gel->cap * 2;
144
1.43k
    gel->edges = fz_realloc_array(ctx, gel->edges, new_cap, fz_edge);
145
1.43k
    gel->cap = new_cap;
146
1.43k
  }
147
148
14.4M
  edge = &gel->edges[gel->len++];
149
150
14.4M
  dy = y1 - y0;
151
14.4M
  dx = x1 - x0;
152
14.4M
  width = fz_absi(dx);
153
154
14.4M
  edge->xdir = dx > 0 ? 1 : -1;
155
14.4M
  edge->ydir = winding;
156
14.4M
  edge->x = x0;
157
14.4M
  edge->y = y0;
158
14.4M
  edge->h = dy;
159
14.4M
  edge->adj_down = dy;
160
161
  /* initial error term going l->r and r->l */
162
14.4M
  if (dx >= 0)
163
10.8M
    edge->e = 0;
164
3.66M
  else
165
3.66M
    edge->e = -dy + 1;
166
167
  /* y-major edge */
168
14.4M
  if (dy >= width) {
169
12.0M
    edge->xmove = 0;
170
12.0M
    edge->adj_up = width;
171
12.0M
  }
172
173
  /* x-major edge */
174
2.38M
  else {
175
2.38M
    edge->xmove = (width / dy) * edge->xdir;
176
2.38M
    edge->adj_up = width % dy;
177
2.38M
  }
178
14.4M
}
179
180
static void
181
fz_insert_gel(fz_context *ctx, fz_rasterizer *ras, float fx0, float fy0, float fx1, float fy1, int rev)
182
172M
{
183
172M
  int x0, y0, x1, y1;
184
172M
  int d, v;
185
172M
  const int hscale = fz_rasterizer_aa_hscale(ras);
186
172M
  const int vscale = fz_rasterizer_aa_vscale(ras);
187
188
172M
  fx0 = floorf(fx0 * hscale);
189
172M
  fx1 = floorf(fx1 * hscale);
190
172M
  fy0 = floorf(fy0 * vscale);
191
172M
  fy1 = floorf(fy1 * vscale);
192
193
  /* Call fz_clamp so that clamping is done in the float domain, THEN
194
   * cast down to an int. Calling fz_clampi causes problems due to the
195
   * implicit cast down from float to int of the first argument
196
   * over/underflowing and flipping sign at extreme values. */
197
172M
  x0 = (int)fz_clamp(fx0, BBOX_MIN * hscale, BBOX_MAX * hscale);
198
172M
  y0 = (int)fz_clamp(fy0, BBOX_MIN * vscale, BBOX_MAX * vscale);
199
172M
  x1 = (int)fz_clamp(fx1, BBOX_MIN * hscale, BBOX_MAX * hscale);
200
172M
  y1 = (int)fz_clamp(fy1, BBOX_MIN * vscale, BBOX_MAX * vscale);
201
202
172M
  d = clip_lerp_y(ras->clip.y0, 0, x0, y0, x1, y1, &v);
203
172M
  if (d == OUTSIDE) return;
204
61.4M
  if (d == LEAVE) { y1 = ras->clip.y0; x1 = v; }
205
61.4M
  if (d == ENTER) { y0 = ras->clip.y0; x0 = v; }
206
207
61.4M
  d = clip_lerp_y(ras->clip.y1, 1, x0, y0, x1, y1, &v);
208
61.4M
  if (d == OUTSIDE) return;
209
31.4M
  if (d == LEAVE) { y1 = ras->clip.y1; x1 = v; }
210
31.4M
  if (d == ENTER) { y0 = ras->clip.y1; x0 = v; }
211
212
31.4M
  d = clip_lerp_x(ras->clip.x0, 0, x0, y0, x1, y1, &v);
213
31.4M
  if (d == OUTSIDE) {
214
2.02M
    x0 = x1 = ras->clip.x0;
215
2.02M
  }
216
31.4M
  if (d == LEAVE) {
217
20.9k
    fz_insert_gel_raw(ctx, ras, ras->clip.x0, v, ras->clip.x0, y1);
218
20.9k
    x1 = ras->clip.x0;
219
20.9k
    y1 = v;
220
20.9k
  }
221
31.4M
  if (d == ENTER) {
222
21.3k
    fz_insert_gel_raw(ctx, ras, ras->clip.x0, y0, ras->clip.x0, v);
223
21.3k
    x0 = ras->clip.x0;
224
21.3k
    y0 = v;
225
21.3k
  }
226
227
31.4M
  d = clip_lerp_x(ras->clip.x1, 1, x0, y0, x1, y1, &v);
228
31.4M
  if (d == OUTSIDE) {
229
17.0M
    x0 = x1 = ras->clip.x1;
230
17.0M
  }
231
31.4M
  if (d == LEAVE) {
232
10.9k
    fz_insert_gel_raw(ctx, ras, ras->clip.x1, v, ras->clip.x1, y1);
233
10.9k
    x1 = ras->clip.x1;
234
10.9k
    y1 = v;
235
10.9k
  }
236
31.4M
  if (d == ENTER) {
237
10.4k
    fz_insert_gel_raw(ctx, ras, ras->clip.x1, y0, ras->clip.x1, v);
238
10.4k
    x0 = ras->clip.x1;
239
10.4k
    y0 = v;
240
10.4k
  }
241
242
31.4M
  fz_insert_gel_raw(ctx, ras, x0, y0, x1, y1);
243
31.4M
}
244
245
static void
246
fz_insert_gel_rect(fz_context *ctx, fz_rasterizer *ras, float fx0, float fy0, float fx1, float fy1)
247
637k
{
248
637k
  int x0, y0, x1, y1;
249
637k
  const int hscale = fz_rasterizer_aa_hscale(ras);
250
637k
  const int vscale = fz_rasterizer_aa_vscale(ras);
251
252
637k
  if (fx0 <= fx1)
253
376k
  {
254
376k
    fx0 = floorf(fx0 * hscale);
255
376k
    fx1 = ceilf(fx1 * hscale);
256
376k
  }
257
260k
  else
258
260k
  {
259
260k
    fx0 = ceilf(fx0 * hscale);
260
260k
    fx1 = floorf(fx1 * hscale);
261
260k
  }
262
637k
  if (fy0 <= fy1)
263
303k
  {
264
303k
    fy0 = floorf(fy0 * vscale);
265
303k
    fy1 = ceilf(fy1 * vscale);
266
303k
  }
267
334k
  else
268
334k
  {
269
334k
    fy0 = ceilf(fy0 * vscale);
270
334k
    fy1 = floorf(fy1 * vscale);
271
334k
  }
272
273
637k
  fx0 = fz_clamp(fx0, ras->clip.x0, ras->clip.x1);
274
637k
  fx1 = fz_clamp(fx1, ras->clip.x0, ras->clip.x1);
275
637k
  fy0 = fz_clamp(fy0, ras->clip.y0, ras->clip.y1);
276
637k
  fy1 = fz_clamp(fy1, ras->clip.y0, ras->clip.y1);
277
278
  /* Call fz_clamp so that clamping is done in the float domain, THEN
279
   * cast down to an int. Calling fz_clampi causes problems due to the
280
   * implicit cast down from float to int of the first argument
281
   * over/underflowing and flipping sign at extreme values. */
282
637k
  x0 = (int)fz_clamp(fx0, BBOX_MIN * hscale, BBOX_MAX * hscale);
283
637k
  y0 = (int)fz_clamp(fy0, BBOX_MIN * vscale, BBOX_MAX * vscale);
284
637k
  x1 = (int)fz_clamp(fx1, BBOX_MIN * hscale, BBOX_MAX * hscale);
285
637k
  y1 = (int)fz_clamp(fy1, BBOX_MIN * vscale, BBOX_MAX * vscale);
286
287
637k
  fz_insert_gel_raw(ctx, ras, x1, y0, x1, y1);
288
637k
  fz_insert_gel_raw(ctx, ras, x0, y1, x0, y0);
289
637k
}
290
291
static int
292
cmpedge(const void *va, const void *vb)
293
4.40M
{
294
4.40M
  const fz_edge *a = va;
295
4.40M
  const fz_edge *b = vb;
296
4.40M
  return a->y - b->y;
297
4.40M
}
298
299
static void
300
sort_gel(fz_context *ctx, fz_gel *gel)
301
358k
{
302
358k
  fz_edge *a = gel->edges;
303
358k
  int n = gel->len;
304
358k
  int h, i, k;
305
358k
  fz_edge t;
306
307
  /* quick sort for long lists */
308
358k
  if (n > 10000)
309
24
  {
310
24
    qsort(a, n, sizeof *a, cmpedge);
311
24
    return;
312
24
  }
313
314
  /* shell sort for short lists */
315
358k
  h = 1;
316
358k
  if (n < 14) {
317
260k
    h = 1;
318
260k
  }
319
98.2k
  else {
320
427k
    while (h < n)
321
329k
      h = 3 * h + 1;
322
98.2k
    h /= 3;
323
98.2k
    h /= 3;
324
98.2k
  }
325
326
850k
  while (h > 0)
327
491k
  {
328
46.4M
    for (i = 0; i < n; i++) {
329
45.9M
      t = a[i];
330
45.9M
      k = i - h;
331
      /* TODO: sort on y major, x minor */
332
82.3M
      while (k >= 0 && a[k].y > t.y) {
333
36.3M
        a[k + h] = a[k];
334
36.3M
        k -= h;
335
36.3M
      }
336
45.9M
      a[k + h] = t;
337
45.9M
    }
338
491k
    h /= 3;
339
491k
  }
340
358k
}
341
342
static int
343
fz_is_rect_gel(fz_context *ctx, fz_rasterizer *ras)
344
116k
{
345
116k
  fz_gel *gel = (fz_gel *)ras;
346
  /* a rectangular path is converted into two vertical edges of identical height */
347
116k
  if (gel->len == 2)
348
74.9k
  {
349
74.9k
    fz_edge *a = gel->edges + 0;
350
74.9k
    fz_edge *b = gel->edges + 1;
351
74.9k
    return a->y == b->y && a->h == b->h &&
352
74.9k
      a->xmove == 0 && a->adj_up == 0 &&
353
74.9k
      b->xmove == 0 && b->adj_up == 0;
354
74.9k
  }
355
41.6k
  return 0;
356
116k
}
357
358
/*
359
 * Active Edge List -- keep track of active edges while sweeping
360
 */
361
362
static void
363
sort_active(fz_edge **a, int n)
364
83.2M
{
365
83.2M
  int h, i, k;
366
83.2M
  fz_edge *t;
367
368
83.2M
  h = 1;
369
83.2M
  if (n < 14) {
370
77.1M
    h = 1;
371
77.1M
  }
372
6.10M
  else {
373
24.8M
    while (h < n)
374
18.7M
      h = 3 * h + 1;
375
6.10M
    h /= 3;
376
6.10M
    h /= 3;
377
6.10M
  }
378
379
172M
  while (h > 0)
380
89.7M
  {
381
919M
    for (i = 0; i < n; i++) {
382
829M
      t = a[i];
383
829M
      k = i - h;
384
863M
      while (k >= 0 && a[k]->x > t->x) {
385
33.3M
        a[k + h] = a[k];
386
33.3M
        k -= h;
387
33.3M
      }
388
829M
      a[k + h] = t;
389
829M
    }
390
391
89.7M
    h /= 3;
392
89.7M
  }
393
83.2M
}
394
395
static int
396
insert_active(fz_context *ctx, fz_gel *gel, int y, int *e_)
397
83.2M
{
398
83.2M
  int h_min = INT_MAX;
399
83.2M
  int e = *e_;
400
401
  /* insert edges that start here */
402
83.2M
  if (e < gel->len && gel->edges[e].y == y)
403
6.31M
  {
404
10.5M
    do {
405
10.5M
      if (gel->alen + 1 == gel->acap) {
406
3.28k
        int newcap = gel->acap + 64;
407
3.28k
        fz_edge **newactive = fz_realloc_array(ctx, gel->active, newcap, fz_edge*);
408
3.28k
        gel->active = newactive;
409
3.28k
        gel->acap = newcap;
410
3.28k
      }
411
10.5M
      gel->active[gel->alen++] = &gel->edges[e++];
412
10.5M
    } while (e < gel->len && gel->edges[e].y == y);
413
6.31M
    *e_ = e;
414
6.31M
  }
415
416
83.2M
  if (e < gel->len)
417
64.3M
    h_min = gel->edges[e].y - y;
418
419
180M
  for (e=0; e < gel->alen; e++)
420
178M
  {
421
178M
    if (gel->active[e]->xmove != 0 || gel->active[e]->adj_up != 0)
422
81.3M
    {
423
81.3M
      h_min = 1;
424
81.3M
      break;
425
81.3M
    }
426
97.4M
    if (gel->active[e]->h < h_min)
427
3.78M
    {
428
3.78M
      h_min = gel->active[e]->h;
429
3.78M
      if (h_min == 1)
430
234k
        break;
431
3.78M
    }
432
97.4M
  }
433
434
  /* shell-sort the edges by increasing x */
435
83.2M
  sort_active(gel->active, gel->alen);
436
437
83.2M
  return h_min;
438
83.2M
}
439
440
static void
441
advance_active(fz_context *ctx, fz_gel *gel, int inc)
442
83.2M
{
443
83.2M
  fz_edge *edge;
444
83.2M
  int i = 0;
445
446
537M
  while (i < gel->alen)
447
454M
  {
448
454M
    edge = gel->active[i];
449
450
454M
    edge->h -= inc;
451
452
    /* terminator! */
453
454M
    if (edge->h == 0) {
454
10.5M
      gel->active[i] = gel->active[--gel->alen];
455
10.5M
    }
456
457
444M
    else {
458
444M
      edge->x += edge->xmove;
459
444M
      edge->e += edge->adj_up;
460
444M
      if (edge->e > 0) {
461
49.6M
        edge->x += edge->xdir;
462
49.6M
        edge->e -= edge->adj_down;
463
49.6M
      }
464
444M
      i ++;
465
444M
    }
466
454M
  }
467
83.2M
}
468
469
/*
470
 * Anti-aliased scan conversion.
471
 */
472
473
static inline void
474
add_span_aa(fz_context *ctx, fz_gel *gel, int *list, int x0, int x1, int xofs, int h)
475
156M
{
476
156M
  int x0pix, x0sub;
477
156M
  int x1pix, x1sub;
478
156M
  const int hscale = fz_rasterizer_aa_hscale(&gel->super);
479
480
156M
  if (x0 == x1)
481
64.3M
    return;
482
483
  /* x between 0 and width of bbox */
484
92.2M
  x0 -= xofs;
485
92.2M
  x1 -= xofs;
486
487
  /* The cast to unsigned below helps the compiler produce faster
488
   * code on ARMs as the multiply by reciprocal trick it uses does not
489
   * need to correct for signedness. */
490
92.2M
  x0pix = ((unsigned int)x0) / hscale;
491
92.2M
  x0sub = ((unsigned int)x0) % hscale;
492
92.2M
  x1pix = ((unsigned int)x1) / hscale;
493
92.2M
  x1sub = ((unsigned int)x1) % hscale;
494
495
92.2M
  if (x0pix == x1pix)
496
18.8M
  {
497
18.8M
    list[x0pix] += h*(x1sub - x0sub);
498
18.8M
    list[x0pix+1] += h*(x0sub - x1sub);
499
18.8M
  }
500
501
73.3M
  else
502
73.3M
  {
503
73.3M
    list[x0pix] += h*(hscale - x0sub);
504
73.3M
    list[x0pix+1] += h*x0sub;
505
73.3M
    list[x1pix] += h*(x1sub - hscale);
506
73.3M
    list[x1pix+1] += h*-x1sub;
507
73.3M
  }
508
92.2M
}
509
510
static inline void
511
non_zero_winding_aa(fz_context *ctx, fz_gel *gel, int *list, int xofs, int h)
512
82.4M
{
513
82.4M
  int winding = 0;
514
82.4M
  int x = 0;
515
82.4M
  int i;
516
517
534M
  for (i = 0; i < gel->alen; i++)
518
452M
  {
519
452M
    if (!winding && (winding + gel->active[i]->ydir))
520
154M
      x = gel->active[i]->x;
521
452M
    if (winding && !(winding + gel->active[i]->ydir))
522
154M
      add_span_aa(ctx, gel, list, x, gel->active[i]->x, xofs, h);
523
452M
    winding += gel->active[i]->ydir;
524
452M
  }
525
82.4M
}
526
527
static inline void
528
even_odd_aa(fz_context *ctx, fz_gel *gel, int *list, int xofs, int h)
529
1.50M
{
530
1.50M
  int even = 0;
531
1.50M
  int x = 0;
532
1.50M
  int i;
533
534
5.95M
  for (i = 0; i < gel->alen; i++)
535
4.44M
  {
536
4.44M
    if (!even)
537
2.22M
      x = gel->active[i]->x;
538
2.22M
    else
539
2.22M
      add_span_aa(ctx, gel, list, x, gel->active[i]->x, xofs, h);
540
4.44M
    even = !even;
541
4.44M
  }
542
1.50M
}
543
544
static inline void
545
undelta_aa(fz_context *ctx, unsigned char * FZ_RESTRICT out, int * FZ_RESTRICT in, int n, int scale)
546
6.62M
{
547
6.62M
  int d = 0;
548
6.62M
  (void)scale; /* Avoid warnings in some builds */
549
550
2.39G
  while (n--)
551
2.39G
  {
552
2.39G
    d += *in++;
553
2.39G
    *out++ = AA_SCALE(scale, d);
554
2.39G
  }
555
6.62M
}
556
557
static inline void
558
blit_aa(fz_pixmap *dst, int x, int y, unsigned char *mp, int w, unsigned char *color, void *fn, fz_overprint *eop)
559
22.9M
{
560
22.9M
  unsigned char *dp;
561
22.9M
  dp = dst->samples + (y - dst->y) * (size_t)dst->stride + (x - dst->x) * (size_t)dst->n;
562
22.9M
  if (color)
563
21.8M
    (*(fz_span_color_painter_t *)fn)(dp, mp, dst->n, w, color, dst->alpha, eop);
564
1.17M
  else
565
1.17M
    (*(fz_span_painter_t *)fn)(dp, dst->alpha, mp, 1, 0, w, 255, eop);
566
22.9M
}
567
568
static void
569
fz_scan_convert_aa(fz_context *ctx, fz_gel *gel, int eofill, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, void *painter, fz_overprint *eop)
570
358k
{
571
358k
  unsigned char *alphas;
572
358k
  int *deltas;
573
358k
  int y, e;
574
358k
  int yd, yc;
575
358k
  int height, h0, rh;
576
358k
  int bcap;
577
358k
  const int hscale = fz_rasterizer_aa_hscale(&gel->super);
578
358k
  const int vscale = fz_rasterizer_aa_vscale(&gel->super);
579
358k
  const int scale = fz_rasterizer_aa_scale(&gel->super);
580
581
358k
  int xmin = fz_idiv(gel->super.bbox.x0, hscale);
582
358k
  int xmax = fz_idiv_up(gel->super.bbox.x1, hscale);
583
584
358k
  int xofs = xmin * hscale;
585
586
358k
  int skipx = clip->x0 - xmin;
587
358k
  int clipn = clip->x1 - clip->x0;
588
589
358k
  if (gel->len == 0)
590
0
    return;
591
592
358k
  assert(xmin < xmax);
593
358k
  assert(clip->x0 >= xmin);
594
358k
  assert(clip->x1 <= xmax);
595
596
358k
  bcap = xmax - xmin + 2; /* big enough for both alphas and deltas */
597
358k
  if (bcap > gel->bcap)
598
6.02k
  {
599
6.02k
    gel->bcap = bcap;
600
6.02k
    fz_free(ctx, gel->alphas);
601
6.02k
    fz_free(ctx, gel->deltas);
602
6.02k
    gel->alphas = NULL;
603
6.02k
    gel->deltas = NULL;
604
6.02k
    gel->alphas = Memento_label(fz_malloc_array(ctx, bcap, unsigned char), "gel_alphas");
605
6.02k
    gel->deltas = Memento_label(fz_malloc_array(ctx, bcap, int), "gel_deltas");
606
6.02k
  }
607
358k
  alphas = gel->alphas;
608
358k
  deltas = gel->deltas;
609
610
358k
  memset(deltas, 0, (xmax - xmin + 1) * sizeof(int));
611
358k
  gel->alen = 0;
612
613
  /* The theory here is that we have a list of the edges (gel) of length
614
   * gel->len. We have an initially empty list of 'active' edges (of
615
   * length gel->alen). As we increase y, we move any edge that is
616
   * active at this point into the active list. We know that any edge
617
   * before index 'e' is either active, or has been retired.
618
   * Once the length of the active list is 0, and e has reached gel->len
619
   * we know we are finished.
620
   *
621
   * As we move through the list, we group fz_aa_vscale 'sub scanlines'
622
   * into single scanlines, and we blit them.
623
   */
624
625
358k
  e = 0;
626
358k
  y = gel->edges[0].y;
627
358k
  yd = fz_idiv(y, vscale);
628
629
  /* Quickly skip to the start of the clip region */
630
358k
  while (yd < clip->y0 && (gel->alen > 0 || e < gel->len))
631
0
  {
632
    /* rh = remaining height = number of subscanlines left to be
633
     * inserted into the current scanline, which will be plotted
634
     * at yd. */
635
0
    rh = (yd+1)*vscale - y;
636
637
    /* height = The number of subscanlines with identical edge
638
     * positions (i.e. 1 if we have any non vertical edges). */
639
0
    height = insert_active(ctx, gel, y, &e);
640
0
    h0 = height;
641
0
    if (h0 >= rh)
642
0
    {
643
      /* We have enough subscanlines to skip to the next
644
       * scanline. */
645
0
      h0 -= rh;
646
0
      yd++;
647
0
    }
648
    /* Skip any whole scanlines we can */
649
0
    while (yd < clip->y0 && h0 >= vscale)
650
0
    {
651
0
      h0 -= vscale;
652
0
      yd++;
653
0
    }
654
    /* If we haven't hit the start of the clip region, then we
655
     * have less than a scanline left. */
656
0
    if (yd < clip->y0)
657
0
    {
658
0
      h0 = 0;
659
0
    }
660
0
    height -= h0;
661
0
    advance_active(ctx, gel, height);
662
663
0
    y += height;
664
0
  }
665
666
  /* Now do the active lines */
667
83.5M
  while (gel->alen > 0 || e < gel->len)
668
83.2M
  {
669
83.2M
    yc = fz_idiv(y, vscale);  /* yc = current scanline */
670
    /* rh = remaining height = number of subscanlines left to be
671
     * inserted into the current scanline, which will be plotted
672
     * at yd. */
673
83.2M
    rh = (yc+1)*vscale - y;
674
83.2M
    if (yc != yd)
675
5.54M
    {
676
5.54M
      undelta_aa(ctx, alphas, deltas, skipx + clipn, scale);
677
5.54M
      blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop);
678
5.54M
      memset(deltas, 0, (skipx + clipn) * sizeof(int));
679
5.54M
    }
680
83.2M
    yd = yc;
681
83.2M
    if (yd >= clip->y1)
682
0
      break;
683
684
    /* height = The number of subscanlines with identical edge
685
     * positions (i.e. 1 if we have any non vertical edges). */
686
83.2M
    height = insert_active(ctx, gel, y, &e);
687
83.2M
    h0 = height;
688
83.2M
    if (h0 > rh)
689
557k
    {
690
557k
      if (rh < vscale)
691
500k
      {
692
        /* We have to finish a scanline off, and we
693
         * have more sub scanlines than will fit into
694
         * it. */
695
500k
        if (eofill)
696
5.93k
          even_odd_aa(ctx, gel, deltas, xofs, rh);
697
494k
        else
698
494k
          non_zero_winding_aa(ctx, gel, deltas, xofs, rh);
699
500k
        undelta_aa(ctx, alphas, deltas, skipx + clipn, scale);
700
500k
        blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop);
701
500k
        memset(deltas, 0, (skipx + clipn) * sizeof(int));
702
500k
        yd++;
703
500k
        if (yd >= clip->y1)
704
0
          break;
705
500k
        h0 -= rh;
706
500k
      }
707
557k
      if (h0 > vscale)
708
223k
      {
709
        /* Calculate the deltas for any completely full
710
         * scanlines. */
711
223k
        h0 -= vscale;
712
223k
        if (eofill)
713
6.55k
          even_odd_aa(ctx, gel, deltas, xofs, vscale);
714
217k
        else
715
217k
          non_zero_winding_aa(ctx, gel, deltas, xofs, vscale);
716
223k
        undelta_aa(ctx, alphas, deltas, skipx + clipn, scale);
717
223k
        do
718
16.5M
        {
719
          /* Do any successive whole scanlines - no need
720
           * to recalculate deltas here. */
721
16.5M
          blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop);
722
16.5M
          yd++;
723
16.5M
          if (yd >= clip->y1)
724
0
            goto clip_ended;
725
16.5M
          h0 -= vscale;
726
16.5M
        }
727
16.5M
        while (h0 > 0);
728
        /* If we have exactly one full scanline left
729
         * to go, then the deltas/alphas are set up
730
         * already. */
731
223k
        if (h0 == 0)
732
46.3k
          goto advance;
733
177k
        memset(deltas, 0, (skipx + clipn) * sizeof(int));
734
177k
        h0 += vscale;
735
177k
      }
736
557k
    }
737
83.1M
    if (eofill)
738
1.49M
      even_odd_aa(ctx, gel, deltas, xofs, h0);
739
81.6M
    else
740
81.6M
      non_zero_winding_aa(ctx, gel, deltas, xofs, h0);
741
83.2M
advance:
742
83.2M
    advance_active(ctx, gel, height);
743
744
83.2M
    y += height;
745
83.2M
  }
746
747
358k
  if (yd < clip->y1)
748
358k
  {
749
358k
    undelta_aa(ctx, alphas, deltas, skipx + clipn, scale);
750
358k
    blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop);
751
358k
  }
752
358k
clip_ended:
753
358k
  ;
754
358k
}
755
756
/*
757
 * Sharp (not anti-aliased) scan conversion
758
 */
759
760
static inline void
761
blit_sharp(int x0, int x1, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop)
762
0
{
763
0
  unsigned char *dp;
764
0
  int da = dst->alpha;
765
0
  x0 = fz_clampi(x0, dst->x, dst->x + dst->w);
766
0
  x1 = fz_clampi(x1, dst->x, dst->x + dst->w);
767
0
  if (x0 < x1)
768
0
  {
769
0
    dp = dst->samples + (y - dst->y) * (size_t)dst->stride + (x0 - dst->x) * (size_t)dst->n;
770
0
    if (color)
771
0
      (*fn)(dp, dst->n, x1 - x0, color, da, eop);
772
0
    else
773
0
      memset(dp, 255, x1-x0);
774
0
  }
775
0
}
776
777
static inline void
778
non_zero_winding_sharp(fz_context *ctx, fz_gel *gel, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop)
779
0
{
780
0
  int winding = 0;
781
0
  int x = 0;
782
0
  int i;
783
0
  for (i = 0; i < gel->alen; i++)
784
0
  {
785
0
    if (!winding && (winding + gel->active[i]->ydir))
786
0
      x = gel->active[i]->x;
787
0
    if (winding && !(winding + gel->active[i]->ydir))
788
0
      blit_sharp(x, gel->active[i]->x, y, clip, dst, color, fn, eop);
789
0
    winding += gel->active[i]->ydir;
790
0
  }
791
0
}
792
793
static inline void
794
even_odd_sharp(fz_context *ctx, fz_gel *gel, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop)
795
0
{
796
0
  int even = 0;
797
0
  int x = 0;
798
0
  int i;
799
0
  for (i = 0; i < gel->alen; i++)
800
0
  {
801
0
    if (!even)
802
0
      x = gel->active[i]->x;
803
0
    else
804
0
      blit_sharp(x, gel->active[i]->x, y, clip, dst, color, fn, eop);
805
0
    even = !even;
806
0
  }
807
0
}
808
809
static void
810
fz_scan_convert_sharp(fz_context *ctx,
811
  fz_gel *gel, int eofill, const fz_irect *clip,
812
  fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop)
813
0
{
814
0
  int e = 0;
815
0
  int y = gel->edges[0].y;
816
0
  int height;
817
818
0
  gel->alen = 0;
819
820
  /* Skip any lines before the clip region */
821
0
  if (y < clip->y0)
822
0
  {
823
0
    while (gel->alen > 0 || e < gel->len)
824
0
    {
825
0
      height = insert_active(ctx, gel, y, &e);
826
0
      y += height;
827
0
      if (y >= clip->y0)
828
0
      {
829
0
        y = clip->y0;
830
0
        break;
831
0
      }
832
0
    }
833
0
  }
834
835
  /* Now process as lines within the clip region */
836
0
  while (gel->alen > 0 || e < gel->len)
837
0
  {
838
0
    height = insert_active(ctx, gel, y, &e);
839
840
0
    if (gel->alen == 0)
841
0
      y += height;
842
0
    else
843
0
    {
844
0
      int h;
845
0
      if (height >= clip->y1 - y)
846
0
        height = clip->y1 - y;
847
848
0
      h = height;
849
0
      while (h--)
850
0
      {
851
0
        if (eofill)
852
0
          even_odd_sharp(ctx, gel, y, clip, dst, color, fn, eop);
853
0
        else
854
0
          non_zero_winding_sharp(ctx, gel, y, clip, dst, color, fn, eop);
855
0
        y++;
856
0
      }
857
0
    }
858
0
    if (y >= clip->y1)
859
0
      break;
860
861
0
    advance_active(ctx, gel, height);
862
0
  }
863
0
}
864
865
static void
866
fz_convert_gel(fz_context *ctx, fz_rasterizer *rast, int eofill, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_overprint *eop)
867
358k
{
868
358k
  fz_gel *gel = (fz_gel *)rast;
869
870
358k
  sort_gel(ctx, gel);
871
872
358k
  if (fz_aa_bits > 0)
873
358k
  {
874
358k
    void *fn;
875
358k
    if (color)
876
316k
      fn = (void *)fz_get_span_color_painter(dst->n, dst->alpha, color, eop);
877
42.2k
    else
878
42.2k
      fn = (void *)fz_get_span_painter(dst->alpha, 1, 0, 255, eop);
879
358k
    if (fn == NULL)
880
0
      return;
881
358k
    fz_scan_convert_aa(ctx, gel, eofill, clip, dst, color, fn, eop);
882
358k
  }
883
0
  else
884
0
  {
885
0
    fz_solid_color_painter_t *fn = fz_get_solid_color_painter(dst->n, color, dst->alpha, eop);
886
0
    assert(fn);
887
0
    if (fn == NULL)
888
0
      return;
889
0
    fz_scan_convert_sharp(ctx, gel, eofill, clip, dst, color, (fz_solid_color_painter_t *)fn, eop);
890
0
  }
891
358k
}
892
893
static const fz_rasterizer_fns gel_rasterizer =
894
{
895
  fz_drop_gel,
896
  fz_reset_gel,
897
  NULL, /* postindex */
898
  fz_insert_gel,
899
  fz_insert_gel_rect,
900
  NULL, /* gap */
901
  fz_convert_gel,
902
  fz_is_rect_gel,
903
  0 /* Not reusable */
904
};
905
906
fz_rasterizer *
907
fz_new_gel(fz_context *ctx)
908
16.9k
{
909
16.9k
  fz_gel *gel;
910
911
16.9k
  gel = fz_new_derived_rasterizer(ctx, fz_gel, &gel_rasterizer);
912
33.8k
  fz_try(ctx)
913
33.8k
  {
914
16.9k
    gel->edges = NULL;
915
16.9k
    gel->cap = 512;
916
16.9k
    gel->len = 0;
917
16.9k
    gel->edges = Memento_label(fz_malloc_array(ctx, gel->cap, fz_edge), "gel_edges");
918
919
16.9k
    gel->acap = 64;
920
16.9k
    gel->alen = 0;
921
16.9k
    gel->active = Memento_label(fz_malloc_array(ctx, gel->acap, fz_edge*), "gel_active");
922
16.9k
  }
923
33.8k
  fz_catch(ctx)
924
0
  {
925
0
    fz_free(ctx, gel->edges);
926
0
    fz_free(ctx, gel);
927
0
    fz_rethrow(ctx);
928
0
  }
929
930
16.9k
  return &gel->super;
931
16.9k
}