Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7640 views
1
#include "mupdf/fitz.h"
2
#include "draw-imp.h"
3
4
#define BBOX_MIN -(1<<20)
5
#define BBOX_MAX (1<<20)
6
7
/* divide and floor towards -inf */
8
static inline int fz_idiv(int a, int b)
9
{
10
return a < 0 ? (a - b + 1) / b : a / b;
11
}
12
13
/* If AA_BITS is defined, then we assume constant N bits of antialiasing. We
14
* will attempt to provide at least that number of bits of accuracy in the
15
* antialiasing (to a maximum of 8). If it is defined to be 0 then no
16
* antialiasing is done. If it is undefined to we will leave the antialiasing
17
* accuracy as a run time choice.
18
*/
19
struct fz_aa_context_s
20
{
21
int hscale;
22
int vscale;
23
int scale;
24
int bits;
25
};
26
27
void fz_new_aa_context(fz_context *ctx)
28
{
29
#ifndef AA_BITS
30
ctx->aa = fz_malloc_struct(ctx, fz_aa_context);
31
ctx->aa->hscale = 17;
32
ctx->aa->vscale = 15;
33
ctx->aa->scale = 256;
34
ctx->aa->bits = 8;
35
36
#define fz_aa_hscale ((ctxaa)->hscale)
37
#define fz_aa_vscale ((ctxaa)->vscale)
38
#define fz_aa_scale ((ctxaa)->scale)
39
#define fz_aa_bits ((ctxaa)->bits)
40
#define AA_SCALE(x) ((x * fz_aa_scale) >> 8)
41
42
#endif
43
}
44
45
void fz_copy_aa_context(fz_context *dst, fz_context *src)
46
{
47
if (dst && dst->aa && src && src->aa)
48
memcpy(dst->aa, src->aa, sizeof(*src->aa));
49
}
50
51
void fz_drop_aa_context(fz_context *ctx)
52
{
53
#ifndef AA_BITS
54
fz_free(ctx, ctx->aa);
55
ctx->aa = NULL;
56
#endif
57
}
58
59
#ifdef AA_BITS
60
61
#if AA_BITS > 6
62
#define AA_SCALE(x) (x)
63
#define fz_aa_hscale 17
64
#define fz_aa_vscale 15
65
#define fz_aa_bits 8
66
67
#elif AA_BITS > 4
68
#define AA_SCALE(x) ((x * 255) >> 6)
69
#define fz_aa_hscale 8
70
#define fz_aa_vscale 8
71
#define fz_aa_bits 6
72
73
#elif AA_BITS > 2
74
#define AA_SCALE(x) (x * 17)
75
#define fz_aa_hscale 5
76
#define fz_aa_vscale 3
77
#define fz_aa_bits 4
78
79
#elif AA_BITS > 0
80
#define AA_SCALE(x) ((x * 255) >> 2)
81
#define fz_aa_hscale 2
82
#define fz_aa_vscale 2
83
#define fz_aa_bits 2
84
85
#else
86
#define AA_SCALE(x) (x * 255)
87
#define fz_aa_hscale 1
88
#define fz_aa_vscale 1
89
#define fz_aa_bits 0
90
91
#endif
92
#endif
93
94
int
95
fz_aa_level(fz_context *ctx)
96
{
97
fz_aa_context *ctxaa = ctx->aa;
98
return fz_aa_bits;
99
}
100
101
void
102
fz_set_aa_level(fz_context *ctx, int level)
103
{
104
fz_aa_context *ctxaa = ctx->aa;
105
#ifdef AA_BITS
106
fz_warn(ctx, "anti-aliasing was compiled with a fixed precision of %d bits", fz_aa_bits);
107
#else
108
if (level > 6)
109
{
110
fz_aa_hscale = 17;
111
fz_aa_vscale = 15;
112
fz_aa_bits = 8;
113
}
114
else if (level > 4)
115
{
116
fz_aa_hscale = 8;
117
fz_aa_vscale = 8;
118
fz_aa_bits = 6;
119
}
120
else if (level > 2)
121
{
122
fz_aa_hscale = 5;
123
fz_aa_vscale = 3;
124
fz_aa_bits = 4;
125
}
126
else if (level > 0)
127
{
128
fz_aa_hscale = 2;
129
fz_aa_vscale = 2;
130
fz_aa_bits = 2;
131
}
132
else
133
{
134
fz_aa_hscale = 1;
135
fz_aa_vscale = 1;
136
fz_aa_bits = 0;
137
}
138
fz_aa_scale = 0xFF00 / (fz_aa_hscale * fz_aa_vscale);
139
#endif
140
}
141
142
/*
143
* Global Edge List -- list of straight path segments for scan conversion
144
*
145
* Stepping along the edges is with Bresenham's line algorithm.
146
*
147
* See Mike Abrash -- Graphics Programming Black Book (notably chapter 40)
148
*/
149
150
typedef struct fz_edge_s fz_edge;
151
152
struct fz_edge_s
153
{
154
int x, e, h, y;
155
int adj_up, adj_down;
156
int xmove;
157
int xdir, ydir; /* -1 or +1 */
158
};
159
160
struct fz_gel_s
161
{
162
fz_rect clip;
163
fz_irect bbox;
164
int cap, len;
165
fz_edge *edges;
166
int acap, alen;
167
fz_edge **active;
168
};
169
170
#ifdef DUMP_GELS
171
static void
172
fz_dump_gel(fz_gel *gel)
173
{
174
int i;
175
176
printf("%d edges\n", gel->len);
177
for (i = 0; i < gel->len; i++)
178
{
179
fz_edge *e = &gel->edges[i];
180
if (e->ydir > 0)
181
printf("%d %d -> %d %d\n", e->x, e->y, e->x + e->h * e->xmove + e->xdir * e->h * e->adj_up / e->adj_down, e->y + e->h);
182
else
183
printf("%d %d -> %d %d\n", e->x + e->h * e->xmove + e->xdir * e->h * e->adj_up / e->adj_down, e->y + e->h, e->x, e->y);
184
}
185
}
186
#endif
187
188
fz_gel *
189
fz_new_gel(fz_context *ctx)
190
{
191
fz_gel *gel;
192
193
gel = fz_malloc_struct(ctx, fz_gel);
194
fz_try(ctx)
195
{
196
gel->edges = NULL;
197
gel->cap = 512;
198
gel->len = 0;
199
gel->edges = fz_malloc_array(ctx, gel->cap, sizeof(fz_edge));
200
201
gel->clip.x0 = gel->clip.y0 = BBOX_MIN;
202
gel->clip.x1 = gel->clip.y1 = BBOX_MAX;
203
204
gel->bbox.x0 = gel->bbox.y0 = BBOX_MAX;
205
gel->bbox.x1 = gel->bbox.y1 = BBOX_MIN;
206
207
gel->acap = 64;
208
gel->alen = 0;
209
gel->active = fz_malloc_array(ctx, gel->acap, sizeof(fz_edge*));
210
}
211
fz_catch(ctx)
212
{
213
if (gel)
214
fz_free(ctx, gel->edges);
215
fz_free(ctx, gel);
216
fz_rethrow(ctx);
217
}
218
219
return gel;
220
}
221
222
void
223
fz_reset_gel(fz_context *ctx, fz_gel *gel, const fz_irect *clip)
224
{
225
fz_aa_context *ctxaa = ctx->aa;
226
227
if (fz_is_infinite_irect(clip))
228
{
229
gel->clip.x0 = gel->clip.y0 = BBOX_MIN;
230
gel->clip.x1 = gel->clip.y1 = BBOX_MAX;
231
}
232
else {
233
gel->clip.x0 = clip->x0 * fz_aa_hscale;
234
gel->clip.x1 = clip->x1 * fz_aa_hscale;
235
gel->clip.y0 = clip->y0 * fz_aa_vscale;
236
gel->clip.y1 = clip->y1 * fz_aa_vscale;
237
}
238
239
gel->bbox.x0 = gel->bbox.y0 = BBOX_MAX;
240
gel->bbox.x1 = gel->bbox.y1 = BBOX_MIN;
241
242
gel->len = 0;
243
gel->alen = 0;
244
}
245
246
void
247
fz_drop_gel(fz_context *ctx, fz_gel *gel)
248
{
249
if (gel == NULL)
250
return;
251
fz_free(ctx, gel->active);
252
fz_free(ctx, gel->edges);
253
fz_free(ctx, gel);
254
}
255
256
fz_irect *
257
fz_bound_gel(fz_context *ctx, const fz_gel *gel, fz_irect *bbox)
258
{
259
fz_aa_context *ctxaa = ctx->aa;
260
if (gel->len == 0)
261
{
262
*bbox = fz_empty_irect;
263
}
264
else
265
{
266
bbox->x0 = fz_idiv(gel->bbox.x0, fz_aa_hscale);
267
bbox->y0 = fz_idiv(gel->bbox.y0, fz_aa_vscale);
268
bbox->x1 = fz_idiv(gel->bbox.x1, fz_aa_hscale) + 1;
269
bbox->y1 = fz_idiv(gel->bbox.y1, fz_aa_vscale) + 1;
270
}
271
return bbox;
272
}
273
274
fz_rect *
275
fz_gel_scissor(fz_context *ctx, const fz_gel *gel, fz_rect *r)
276
{
277
fz_aa_context *ctxaa = ctx->aa;
278
279
r->x0 = gel->clip.x0 / fz_aa_hscale;
280
r->x1 = gel->clip.x1 / fz_aa_vscale;
281
r->y0 = gel->clip.y0 / fz_aa_hscale;
282
r->y1 = gel->clip.y1 / fz_aa_vscale;
283
284
return r;
285
}
286
287
enum { INSIDE, OUTSIDE, LEAVE, ENTER };
288
289
#define clip_lerp_y(v,m,x0,y0,x1,y1,t) clip_lerp_x(v,m,y0,x0,y1,x1,t)
290
291
static int
292
clip_lerp_x(int val, int m, int x0, int y0, int x1, int y1, int *out)
293
{
294
int v0out = m ? x0 > val : x0 < val;
295
int v1out = m ? x1 > val : x1 < val;
296
297
if (v0out + v1out == 0)
298
return INSIDE;
299
300
if (v0out + v1out == 2)
301
return OUTSIDE;
302
303
if (v1out)
304
{
305
*out = y0 + (int)(((float)(y1 - y0)) * (val - x0) / (x1 - x0));
306
return LEAVE;
307
}
308
309
else
310
{
311
*out = y1 + (int)(((float)(y0 - y1)) * (val - x1) / (x0 - x1));
312
return ENTER;
313
}
314
}
315
316
static void
317
fz_insert_gel_raw(fz_context *ctx, fz_gel *gel, int x0, int y0, int x1, int y1)
318
{
319
fz_edge *edge;
320
int dx, dy;
321
int winding;
322
int width;
323
int tmp;
324
325
if (y0 == y1)
326
return;
327
328
if (y0 > y1) {
329
winding = -1;
330
tmp = x0; x0 = x1; x1 = tmp;
331
tmp = y0; y0 = y1; y1 = tmp;
332
}
333
else
334
winding = 1;
335
336
if (x0 < gel->bbox.x0) gel->bbox.x0 = x0;
337
if (x0 > gel->bbox.x1) gel->bbox.x1 = x0;
338
if (x1 < gel->bbox.x0) gel->bbox.x0 = x1;
339
if (x1 > gel->bbox.x1) gel->bbox.x1 = x1;
340
341
if (y0 < gel->bbox.y0) gel->bbox.y0 = y0;
342
if (y1 > gel->bbox.y1) gel->bbox.y1 = y1;
343
344
if (gel->len + 1 == gel->cap) {
345
int new_cap = gel->cap * 2;
346
gel->edges = fz_resize_array(ctx, gel->edges, new_cap, sizeof(fz_edge));
347
gel->cap = new_cap;
348
}
349
350
edge = &gel->edges[gel->len++];
351
352
dy = y1 - y0;
353
dx = x1 - x0;
354
width = fz_absi(dx);
355
356
edge->xdir = dx > 0 ? 1 : -1;
357
edge->ydir = winding;
358
edge->x = x0;
359
edge->y = y0;
360
edge->h = dy;
361
edge->adj_down = dy;
362
363
/* initial error term going l->r and r->l */
364
if (dx >= 0)
365
edge->e = 0;
366
else
367
edge->e = -dy + 1;
368
369
/* y-major edge */
370
if (dy >= width) {
371
edge->xmove = 0;
372
edge->adj_up = width;
373
}
374
375
/* x-major edge */
376
else {
377
edge->xmove = (width / dy) * edge->xdir;
378
edge->adj_up = width % dy;
379
}
380
}
381
382
void
383
fz_insert_gel(fz_context *ctx, fz_gel *gel, float fx0, float fy0, float fx1, float fy1)
384
{
385
int x0, y0, x1, y1;
386
int d, v;
387
fz_aa_context *ctxaa = ctx->aa;
388
389
fx0 = floorf(fx0 * fz_aa_hscale);
390
fx1 = floorf(fx1 * fz_aa_hscale);
391
fy0 = floorf(fy0 * fz_aa_vscale);
392
fy1 = floorf(fy1 * fz_aa_vscale);
393
394
/* Call fz_clamp so that clamping is done in the float domain, THEN
395
* cast down to an int. Calling fz_clampi causes problems due to the
396
* implicit cast down from float to int of the first argument
397
* over/underflowing and flipping sign at extreme values. */
398
x0 = (int)fz_clamp(fx0, BBOX_MIN * fz_aa_hscale, BBOX_MAX * fz_aa_hscale);
399
y0 = (int)fz_clamp(fy0, BBOX_MIN * fz_aa_vscale, BBOX_MAX * fz_aa_vscale);
400
x1 = (int)fz_clamp(fx1, BBOX_MIN * fz_aa_hscale, BBOX_MAX * fz_aa_hscale);
401
y1 = (int)fz_clamp(fy1, BBOX_MIN * fz_aa_vscale, BBOX_MAX * fz_aa_vscale);
402
403
d = clip_lerp_y(gel->clip.y0, 0, x0, y0, x1, y1, &v);
404
if (d == OUTSIDE) return;
405
if (d == LEAVE) { y1 = gel->clip.y0; x1 = v; }
406
if (d == ENTER) { y0 = gel->clip.y0; x0 = v; }
407
408
d = clip_lerp_y(gel->clip.y1, 1, x0, y0, x1, y1, &v);
409
if (d == OUTSIDE) return;
410
if (d == LEAVE) { y1 = gel->clip.y1; x1 = v; }
411
if (d == ENTER) { y0 = gel->clip.y1; x0 = v; }
412
413
d = clip_lerp_x(gel->clip.x0, 0, x0, y0, x1, y1, &v);
414
if (d == OUTSIDE) {
415
x0 = x1 = gel->clip.x0;
416
}
417
if (d == LEAVE) {
418
fz_insert_gel_raw(ctx, gel, gel->clip.x0, v, gel->clip.x0, y1);
419
x1 = gel->clip.x0;
420
y1 = v;
421
}
422
if (d == ENTER) {
423
fz_insert_gel_raw(ctx, gel, gel->clip.x0, y0, gel->clip.x0, v);
424
x0 = gel->clip.x0;
425
y0 = v;
426
}
427
428
d = clip_lerp_x(gel->clip.x1, 1, x0, y0, x1, y1, &v);
429
if (d == OUTSIDE) {
430
x0 = x1 = gel->clip.x1;
431
}
432
if (d == LEAVE) {
433
fz_insert_gel_raw(ctx, gel, gel->clip.x1, v, gel->clip.x1, y1);
434
x1 = gel->clip.x1;
435
y1 = v;
436
}
437
if (d == ENTER) {
438
fz_insert_gel_raw(ctx, gel, gel->clip.x1, y0, gel->clip.x1, v);
439
x0 = gel->clip.x1;
440
y0 = v;
441
}
442
443
fz_insert_gel_raw(ctx, gel, x0, y0, x1, y1);
444
}
445
446
void
447
fz_insert_gel_rect(fz_context *ctx, fz_gel *gel, float fx0, float fy0, float fx1, float fy1)
448
{
449
int x0, y0, x1, y1;
450
fz_aa_context *ctxaa = ctx->aa;
451
452
if (fx0 <= fx1)
453
{
454
fx0 = floorf(fx0 * fz_aa_hscale);
455
fx1 = ceilf(fx1 * fz_aa_hscale);
456
}
457
else
458
{
459
fx0 = ceilf(fx0 * fz_aa_hscale);
460
fx1 = floorf(fx1 * fz_aa_hscale);
461
}
462
if (fy0 <= fy1)
463
{
464
fy0 = floorf(fy0 * fz_aa_vscale);
465
fy1 = ceilf(fy1 * fz_aa_vscale);
466
}
467
else
468
{
469
fy0 = ceilf(fy0 * fz_aa_vscale);
470
fy1 = floorf(fy1 * fz_aa_vscale);
471
}
472
473
fx0 = fz_clamp(fx0, gel->clip.x0, gel->clip.x1);
474
fx1 = fz_clamp(fx1, gel->clip.x0, gel->clip.x1);
475
fy0 = fz_clamp(fy0, gel->clip.y0, gel->clip.y1);
476
fy1 = fz_clamp(fy1, gel->clip.y0, gel->clip.y1);
477
478
/* Call fz_clamp so that clamping is done in the float domain, THEN
479
* cast down to an int. Calling fz_clampi causes problems due to the
480
* implicit cast down from float to int of the first argument
481
* over/underflowing and flipping sign at extreme values. */
482
x0 = (int)fz_clamp(fx0, BBOX_MIN * fz_aa_hscale, BBOX_MAX * fz_aa_hscale);
483
y0 = (int)fz_clamp(fy0, BBOX_MIN * fz_aa_vscale, BBOX_MAX * fz_aa_vscale);
484
x1 = (int)fz_clamp(fx1, BBOX_MIN * fz_aa_hscale, BBOX_MAX * fz_aa_hscale);
485
y1 = (int)fz_clamp(fy1, BBOX_MIN * fz_aa_vscale, BBOX_MAX * fz_aa_vscale);
486
487
fz_insert_gel_raw(ctx, gel, x1, y0, x1, y1);
488
fz_insert_gel_raw(ctx, gel, x0, y1, x0, y0);
489
}
490
491
static int
492
cmpedge(const void *va, const void *vb)
493
{
494
const fz_edge *a = va;
495
const fz_edge *b = vb;
496
return a->y - b->y;
497
}
498
499
void
500
fz_sort_gel(fz_context *ctx, fz_gel *gel)
501
{
502
fz_edge *a = gel->edges;
503
int n = gel->len;
504
int h, i, k;
505
fz_edge t;
506
507
/* quick sort for long lists */
508
if (n > 10000)
509
{
510
qsort(a, n, sizeof *a, cmpedge);
511
#ifdef DUMP_GELS
512
fz_dump_gel(gel);
513
#endif
514
return;
515
}
516
517
/* shell sort for short lists */
518
h = 1;
519
if (n < 14) {
520
h = 1;
521
}
522
else {
523
while (h < n)
524
h = 3 * h + 1;
525
h /= 3;
526
h /= 3;
527
}
528
529
while (h > 0)
530
{
531
for (i = 0; i < n; i++) {
532
t = a[i];
533
k = i - h;
534
/* TODO: sort on y major, x minor */
535
while (k >= 0 && a[k].y > t.y) {
536
a[k + h] = a[k];
537
k -= h;
538
}
539
a[k + h] = t;
540
}
541
h /= 3;
542
}
543
544
#ifdef DUMP_GELS
545
fz_dump_gel(gel);
546
#endif
547
}
548
549
int
550
fz_is_rect_gel(fz_context *ctx, fz_gel *gel)
551
{
552
/* a rectangular path is converted into two vertical edges of identical height */
553
if (gel->len == 2)
554
{
555
fz_edge *a = gel->edges + 0;
556
fz_edge *b = gel->edges + 1;
557
return a->y == b->y && a->h == b->h &&
558
a->xmove == 0 && a->adj_up == 0 &&
559
b->xmove == 0 && b->adj_up == 0;
560
}
561
return 0;
562
}
563
564
/*
565
* Active Edge List -- keep track of active edges while sweeping
566
*/
567
568
static void
569
sort_active(fz_edge **a, int n)
570
{
571
int h, i, k;
572
fz_edge *t;
573
574
h = 1;
575
if (n < 14) {
576
h = 1;
577
}
578
else {
579
while (h < n)
580
h = 3 * h + 1;
581
h /= 3;
582
h /= 3;
583
}
584
585
while (h > 0)
586
{
587
for (i = 0; i < n; i++) {
588
t = a[i];
589
k = i - h;
590
while (k >= 0 && a[k]->x > t->x) {
591
a[k + h] = a[k];
592
k -= h;
593
}
594
a[k + h] = t;
595
}
596
597
h /= 3;
598
}
599
}
600
601
static int
602
insert_active(fz_context *ctx, fz_gel *gel, int y, int *e_)
603
{
604
int h_min = INT_MAX;
605
int e = *e_;
606
607
/* insert edges that start here */
608
if (e < gel->len && gel->edges[e].y == y)
609
{
610
do {
611
if (gel->alen + 1 == gel->acap) {
612
int newcap = gel->acap + 64;
613
fz_edge **newactive = fz_resize_array(ctx, gel->active, newcap, sizeof(fz_edge*));
614
gel->active = newactive;
615
gel->acap = newcap;
616
}
617
gel->active[gel->alen++] = &gel->edges[e++];
618
} while (e < gel->len && gel->edges[e].y == y);
619
*e_ = e;
620
}
621
622
if (e < gel->len)
623
h_min = gel->edges[e].y - y;
624
625
for (e=0; e < gel->alen; e++)
626
{
627
if (gel->active[e]->xmove != 0 || gel->active[e]->adj_up != 0)
628
{
629
h_min = 1;
630
break;
631
}
632
if (gel->active[e]->h < h_min)
633
{
634
h_min = gel->active[e]->h;
635
if (h_min == 1)
636
break;
637
}
638
}
639
640
/* shell-sort the edges by increasing x */
641
sort_active(gel->active, gel->alen);
642
643
return h_min;
644
}
645
646
static void
647
advance_active(fz_context *ctx, fz_gel *gel, int inc)
648
{
649
fz_edge *edge;
650
int i = 0;
651
652
while (i < gel->alen)
653
{
654
edge = gel->active[i];
655
656
edge->h -= inc;
657
658
/* terminator! */
659
if (edge->h == 0) {
660
gel->active[i] = gel->active[--gel->alen];
661
}
662
663
else {
664
edge->x += edge->xmove;
665
edge->e += edge->adj_up;
666
if (edge->e > 0) {
667
edge->x += edge->xdir;
668
edge->e -= edge->adj_down;
669
}
670
i ++;
671
}
672
}
673
}
674
675
/*
676
* Anti-aliased scan conversion.
677
*/
678
679
static inline void
680
add_span_aa(fz_aa_context *ctxaa, int *list, int x0, int x1, int xofs, int h)
681
{
682
int x0pix, x0sub;
683
int x1pix, x1sub;
684
685
if (x0 == x1)
686
return;
687
688
/* x between 0 and width of bbox */
689
x0 -= xofs;
690
x1 -= xofs;
691
692
/* The cast to unsigned below helps the compiler produce faster
693
* code on ARMs as the multiply by reciprocal trick it uses does not
694
* need to correct for signedness. */
695
x0pix = ((unsigned int)x0) / fz_aa_hscale;
696
x0sub = ((unsigned int)x0) % fz_aa_hscale;
697
x1pix = ((unsigned int)x1) / fz_aa_hscale;
698
x1sub = ((unsigned int)x1) % fz_aa_hscale;
699
700
if (x0pix == x1pix)
701
{
702
list[x0pix] += h*(x1sub - x0sub);
703
list[x0pix+1] += h*(x0sub - x1sub);
704
}
705
706
else
707
{
708
list[x0pix] += h*(fz_aa_hscale - x0sub);
709
list[x0pix+1] += h*x0sub;
710
list[x1pix] += h*(x1sub - fz_aa_hscale);
711
list[x1pix+1] += h*-x1sub;
712
}
713
}
714
715
static inline void
716
non_zero_winding_aa(fz_context *ctx, fz_gel *gel, int *list, int xofs, int h)
717
{
718
fz_aa_context *ctxaa = ctx->aa;
719
int winding = 0;
720
int x = 0;
721
int i;
722
723
for (i = 0; i < gel->alen; i++)
724
{
725
if (!winding && (winding + gel->active[i]->ydir))
726
x = gel->active[i]->x;
727
if (winding && !(winding + gel->active[i]->ydir))
728
add_span_aa(ctxaa, list, x, gel->active[i]->x, xofs, h);
729
winding += gel->active[i]->ydir;
730
}
731
}
732
733
static inline void
734
even_odd_aa(fz_context *ctx, fz_gel *gel, int *list, int xofs, int h)
735
{
736
fz_aa_context *ctxaa = ctx->aa;
737
int even = 0;
738
int x = 0;
739
int i;
740
741
for (i = 0; i < gel->alen; i++)
742
{
743
if (!even)
744
x = gel->active[i]->x;
745
else
746
add_span_aa(ctxaa, list, x, gel->active[i]->x, xofs, h);
747
even = !even;
748
}
749
}
750
751
static inline void
752
undelta_aa(fz_aa_context *ctxaa, unsigned char * restrict out, int * restrict in, int n)
753
{
754
int d = 0;
755
while (n--)
756
{
757
d += *in++;
758
*out++ = AA_SCALE(d);
759
}
760
}
761
762
static inline void
763
blit_aa(fz_pixmap *dst, int x, int y, unsigned char *mp, int w, unsigned char *color)
764
{
765
unsigned char *dp;
766
dp = dst->samples + (unsigned int)(( (y - dst->y) * dst->w + (x - dst->x) ) * dst->n);
767
if (color)
768
fz_paint_span_with_color(dp, mp, dst->n, w, color);
769
else
770
fz_paint_span(dp, mp, 1, w, 255);
771
}
772
773
static void
774
fz_scan_convert_aa(fz_context *ctx, fz_gel *gel, int eofill, const fz_irect *clip, fz_pixmap *dst, unsigned char *color)
775
{
776
fz_aa_context *ctxaa = ctx->aa;
777
unsigned char *alphas;
778
int *deltas;
779
int y, e;
780
int yd, yc;
781
int height, h0, rh;
782
783
int xmin = fz_idiv(gel->bbox.x0, fz_aa_hscale);
784
int xmax = fz_idiv(gel->bbox.x1, fz_aa_hscale) + 1;
785
786
int xofs = xmin * fz_aa_hscale;
787
788
int skipx = clip->x0 - xmin;
789
int clipn = clip->x1 - clip->x0;
790
791
if (gel->len == 0)
792
return;
793
794
assert(clip->x0 >= xmin);
795
assert(clip->x1 <= xmax);
796
797
alphas = fz_malloc_no_throw(ctx, xmax - xmin + 1);
798
deltas = fz_malloc_no_throw(ctx, (xmax - xmin + 1) * sizeof(int));
799
if (alphas == NULL || deltas == NULL)
800
{
801
fz_free(ctx, alphas);
802
fz_free(ctx, deltas);
803
fz_throw(ctx, FZ_ERROR_GENERIC, "scan conversion failed (malloc failure)");
804
}
805
memset(deltas, 0, (xmax - xmin + 1) * sizeof(int));
806
gel->alen = 0;
807
808
/* The theory here is that we have a list of the edges (gel) of length
809
* gel->len. We have an initially empty list of 'active' edges (of
810
* length gel->alen). As we increase y, we move any edge that is
811
* active at this point into the active list. We know that any edge
812
* before index 'e' is either active, or has been retired.
813
* Once the length of the active list is 0, and e has reached gel->len
814
* we know we are finished.
815
*
816
* As we move through the list, we group fz_aa_vscale 'sub scanlines'
817
* into single scanlines, and we blit them.
818
*/
819
820
e = 0;
821
y = gel->edges[0].y;
822
yd = fz_idiv(y, fz_aa_vscale);
823
824
/* Quickly skip to the start of the clip region */
825
while (yd < clip->y0 && (gel->alen > 0 || e < gel->len))
826
{
827
/* rh = remaining height = number of subscanlines left to be
828
* inserted into the current scanline, which will be plotted
829
* at yd. */
830
rh = (yd+1)*fz_aa_vscale - y;
831
832
/* height = The number of subscanlines with identical edge
833
* positions (i.e. 1 if we have any non vertical edges). */
834
height = insert_active(ctx, gel, y, &e);
835
h0 = height;
836
if (h0 >= rh)
837
{
838
/* We have enough subscanlines to skip to the next
839
* scanline. */
840
h0 -= rh;
841
yd++;
842
}
843
/* Skip any whole scanlines we can */
844
while (yd < clip->y0 && h0 >= fz_aa_vscale)
845
{
846
h0 -= fz_aa_vscale;
847
yd++;
848
}
849
/* If we haven't hit the start of the clip region, then we
850
* have less than a scanline left. */
851
if (yd < clip->y0)
852
{
853
h0 = 0;
854
}
855
height -= h0;
856
advance_active(ctx, gel, height);
857
858
y += height;
859
}
860
861
/* Now do the active lines */
862
while (gel->alen > 0 || e < gel->len)
863
{
864
yc = fz_idiv(y, fz_aa_vscale); /* yc = current scanline */
865
/* rh = remaining height = number of subscanlines left to be
866
* inserted into the current scanline, which will be plotted
867
* at yd. */
868
rh = (yc+1)*fz_aa_vscale - y;
869
if (yc != yd)
870
{
871
undelta_aa(ctxaa, alphas, deltas, skipx + clipn);
872
blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color);
873
memset(deltas, 0, (skipx + clipn) * sizeof(int));
874
}
875
yd = yc;
876
if (yd >= clip->y1)
877
break;
878
879
/* height = The number of subscanlines with identical edge
880
* positions (i.e. 1 if we have any non vertical edges). */
881
height = insert_active(ctx, gel, y, &e);
882
h0 = height;
883
if (h0 > rh)
884
{
885
if (rh < fz_aa_vscale)
886
{
887
/* We have to finish a scanline off, and we
888
* have more sub scanlines than will fit into
889
* it. */
890
if (eofill)
891
even_odd_aa(ctx, gel, deltas, xofs, rh);
892
else
893
non_zero_winding_aa(ctx, gel, deltas, xofs, rh);
894
undelta_aa(ctxaa, alphas, deltas, skipx + clipn);
895
blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color);
896
memset(deltas, 0, (skipx + clipn) * sizeof(int));
897
yd++;
898
if (yd >= clip->y1)
899
break;
900
h0 -= rh;
901
}
902
if (h0 > fz_aa_vscale)
903
{
904
/* Calculate the deltas for any completely full
905
* scanlines. */
906
h0 -= fz_aa_vscale;
907
if (eofill)
908
even_odd_aa(ctx, gel, deltas, xofs, fz_aa_vscale);
909
else
910
non_zero_winding_aa(ctx, gel, deltas, xofs, fz_aa_vscale);
911
undelta_aa(ctxaa, alphas, deltas, skipx + clipn);
912
do
913
{
914
/* Do any successive whole scanlines - no need
915
* to recalculate deltas here. */
916
blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color);
917
yd++;
918
if (yd >= clip->y1)
919
goto clip_ended;
920
h0 -= fz_aa_vscale;
921
}
922
while (h0 > 0);
923
/* If we have exactly one full scanline left
924
* to go, then the deltas/alphas are set up
925
* already. */
926
if (h0 == 0)
927
goto advance;
928
memset(deltas, 0, (skipx + clipn) * sizeof(int));
929
h0 += fz_aa_vscale;
930
}
931
}
932
if (eofill)
933
even_odd_aa(ctx, gel, deltas, xofs, h0);
934
else
935
non_zero_winding_aa(ctx, gel, deltas, xofs, h0);
936
advance:
937
advance_active(ctx, gel, height);
938
939
y += height;
940
}
941
942
if (yd < clip->y1)
943
{
944
undelta_aa(ctxaa, alphas, deltas, skipx + clipn);
945
blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color);
946
}
947
clip_ended:
948
fz_free(ctx, deltas);
949
fz_free(ctx, alphas);
950
}
951
952
/*
953
* Sharp (not anti-aliased) scan conversion
954
*/
955
956
static inline void
957
blit_sharp(int x0, int x1, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color)
958
{
959
unsigned char *dp;
960
x0 = fz_clampi(x0, dst->x, dst->x + dst->w);
961
x1 = fz_clampi(x1, dst->x, dst->x + dst->w);
962
if (x0 < x1)
963
{
964
dp = dst->samples + (unsigned int)(( (y - dst->y) * dst->w + (x0 - dst->x) ) * dst->n);
965
if (color)
966
fz_paint_solid_color(dp, dst->n, x1 - x0, color);
967
else
968
fz_paint_solid_alpha(dp, x1 - x0, 255);
969
}
970
}
971
972
static inline void
973
non_zero_winding_sharp(fz_context *ctx, fz_gel *gel, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color)
974
{
975
int winding = 0;
976
int x = 0;
977
int i;
978
for (i = 0; i < gel->alen; i++)
979
{
980
if (!winding && (winding + gel->active[i]->ydir))
981
x = gel->active[i]->x;
982
if (winding && !(winding + gel->active[i]->ydir))
983
blit_sharp(x, gel->active[i]->x, y, clip, dst, color);
984
winding += gel->active[i]->ydir;
985
}
986
}
987
988
static inline void
989
even_odd_sharp(fz_context *ctx, fz_gel *gel, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color)
990
{
991
int even = 0;
992
int x = 0;
993
int i;
994
for (i = 0; i < gel->alen; i++)
995
{
996
if (!even)
997
x = gel->active[i]->x;
998
else
999
blit_sharp(x, gel->active[i]->x, y, clip, dst, color);
1000
even = !even;
1001
}
1002
}
1003
1004
static void
1005
fz_scan_convert_sharp(fz_context *ctx,
1006
fz_gel *gel, int eofill, const fz_irect *clip,
1007
fz_pixmap *dst, unsigned char *color)
1008
{
1009
int e = 0;
1010
int y = gel->edges[0].y;
1011
int height;
1012
1013
gel->alen = 0;
1014
1015
/* Skip any lines before the clip region */
1016
if (y < clip->y0)
1017
{
1018
while (gel->alen > 0 || e < gel->len)
1019
{
1020
height = insert_active(ctx, gel, y, &e);
1021
y += height;
1022
if (y >= clip->y0)
1023
{
1024
y = clip->y0;
1025
break;
1026
}
1027
}
1028
}
1029
1030
/* Now process as lines within the clip region */
1031
while (gel->alen > 0 || e < gel->len)
1032
{
1033
height = insert_active(ctx, gel, y, &e);
1034
1035
if (gel->alen == 0)
1036
y += height;
1037
else
1038
{
1039
int h;
1040
if (height >= clip->y1 - y)
1041
height = clip->y1 - y;
1042
1043
h = height;
1044
while (h--)
1045
{
1046
if (eofill)
1047
even_odd_sharp(ctx, gel, y, clip, dst, color);
1048
else
1049
non_zero_winding_sharp(ctx, gel, y, clip, dst, color);
1050
y++;
1051
}
1052
}
1053
if (y >= clip->y1)
1054
break;
1055
1056
advance_active(ctx, gel, height);
1057
}
1058
}
1059
1060
void
1061
fz_scan_convert(fz_context *ctx, fz_gel *gel, int eofill, const fz_irect *clip, fz_pixmap *dst, unsigned char *color)
1062
{
1063
fz_aa_context *ctxaa = ctx->aa;
1064
fz_irect local_clip;
1065
1066
if (fz_is_empty_irect(fz_intersect_irect(fz_pixmap_bbox_no_ctx(dst, &local_clip), clip)))
1067
return;
1068
1069
if (fz_aa_bits > 0)
1070
fz_scan_convert_aa(ctx, gel, eofill, &local_clip, dst, color);
1071
else
1072
fz_scan_convert_sharp(ctx, gel, eofill, &local_clip, dst, color);
1073
}
1074
1075